## Friday, October 25, 2019

### RSA is too slow, very expensive and less efficient

With symmetric ciphers, encryption and decryption speed can be several gigabytes per seconds on a common PC core, with RSA encryption, on comparable hardware, we are talking tens of thousands encryptions per second, and only few hundreds of decryption per seconds, for common key sizes, and small messages (like 1 bit to 250 bytes, way enough for a session keys and authenticators).

The distinguishing technique used in public key cryptography is the use of asymmetric key algorithms, where the key used to encrypt a message is not the same as the key used to decrypt it. Each user has a pair of cryptographic keys—a public encryption key and a private decryption key. The publicly available encrypting-key is widely distributed, while the private decrypting-key is known only to the recipient. Messages are encrypted with the recipient's public key and can only be decrypted with the corresponding private key. The keys are related mathematically, but the private key cannot feasibly (ie. in actual or projected practice) be derived from the public key. The discovery of algorithms that could produce public/private key pairs revolutionized the practice of cryptography beginning in the middle 1970s. RSA has some operational constraints. With the most used variant (the one known as PKCS#1 v1.5), if the size of the RSA key is "1024 bits" (meaning that the central mathematical component of the key pair is a 1024-bit integer), then RSA can encrypt a message up to 117 bytes in length, and yields an encrypted message of length 128 bytes. That limited size, and the size increase when encrypting, are unavoidable consequences of the mathematical structure of the RSA encryption process. Due to these constraints, we do not usually encrypt data directly with RSA; instead, we select a small sequence of random bytes, which we call session key. We encrypt the session key with RSA; and then we use the session key with a symmetric encryption algorithm to process the whole message. This is called hybrid encryption.

• Provides authenticity of the source Allows message authentication) As public key encryption allows using digital signatures, message recipients will be able to verify messages to be truly coming from a particular sender.
• It is convenient (Private keys are never exposed) - Asymmetric encryption solves the problem of distributing keys for encryption, with everyone publishing their public keys, while private keys being kept secret.
• It allows for non-repudiation Digitally signed messages are like physically signed documents. Basically, it is like acknowledging a message, and therefore, the sender will not be able to deny it.
• It detects tamperingWith digital signatures in public key encryption, message recipients can detect if a message was altered in transit.

• It is a slow process - Very slow compared to symmetric cryptography (100 to 1000 times slower)Public key encryption in this method is slow compared with symmetric encryption, which means that it is not suitable for decrypting bulk messages.
• Size of encrypted data limited by performance considerations Not suitable for encrypting large amounts of data
• Public key management (public keys are not authenticated)Basically, no one absolutely knows that a public key belongs to the individual it specifies, which means that users will have to verify that their public keys truly belong to them.
• It risks loss of private key, which may be irreparable When you lose your private key, your received messages will not be decrypted.
• It risks widespread security compromiseIf your private key is identified by an attacker, all of your messages can be read by him/her.
The most widely-used algorithm used in symmetric key cryptography is AES (Advanced Encryption Standard). The most common asymmetric encryption algorithms is the Diffie-Hellman key exchange, which allows two parties to exchange cryptographic keys in a secure manner regardless of whether the communication channel is public or private. The RSA (Rivest, Shamir and Adleman) asymmetric algorithm is another widely used asymmetric encryption.Again , if there will be a option to choose one of the Asymmetric algorithm then we will choose RSA instead of Diffie-Hellman.

Diffie-Hellman : Actually it mathematically helps two parties which involves in the communication to generate a Secret Key(symmetric key) at each side which are identical and same. this key is used for encrypt and decrypt at both sides. Diffe-Hellman is used for key exchange using the concept of primitive root and then both parties use that common key for subsequent data exchange using symmetric key encryption.

RSA : Actually it mathematically helps two parties which involves in the communication to generate a pair of keys (public and private) at each side, where public key is used for encrypt and private key is used for decrypt. RSA algorithm is used for actual asymmetric key encryption . It generates public-private key pair and then use them for exchanging data.RSA isn't really built to encrypt large pieces of plaintext

RSA isn't well suited for encrypting large data sizes. The usual approach would be to use a symmetric cipher like AES or Triple-DES, and then to use RSA just to encrypt the AES/Triple-DES key (like PGP a Hybrid Crypto System (Symmetric & Asymmetric) ). RSA can only be used to encrypt data that is no longer than the RSA key size.  With a key size of 2048 bits that would be somewhat less than 256 bytes. We could use an even larger key size, but that would make the process rather slow. Each RSA "round" can encrypt 117 bytes of data, and to encrypt more, you'd have to use some chaining mode. Currently, this means extra overhead, slowness (remember, RSA is pretty slow), and security uncertainty (RSA-chaningMode hasn't been scrutinized as other types of encryption schemes).

Hybrid approach of using RSA
• Not only is RSA too slow to use on bulk data, but it even has certain weaknesses that can be exploited in some special  cases of particular kinds of messages that are fed to the RSA cipher, even for large keys.
• These special cases can be avoided by using the hybrid approach of using RSA to encrypt random session keys for a conventional cipher, like PGP does. So the bottom line is this: Using pure RSA on bulk data is the wrong approach, period. It's too slow, it's not stronger, and may even be weaker.
• The additional mechanisms performed by PGP (compression and random key generation) rather than any difference in the performance of the encryption algorithms themselves.
• We also note that Diffie-Hellman marginly outperforms RSA, which is directly in contradiction with earlier findings where RSA was significantly faster. This second point might be the result of the initialisation phase of RSA being more costly and so on a very small amount of data (the randomly generated key), it becomes less efficient than Diffie-Hellman.
There are two main reasons why asymmetric cryptography is practically never used to directly encrypt significant amount of data:
1. Size of cryptogram: symmetric encryption does not increase the size of the cryptogram (asymptotically), but asymmetric encryption does. If we take the example of RSAES-OAEP in PKCS#1v2 with a 1024-bit key and 160-bit SHA-1 hash, a 1024-bit cryptogram can convey a maximum of 688 bit of useful information. Thus data enciphered in this way would cost 49% more space to store, or more time to move over a given link.
2. Performance: on a modern CPU with hardware AES support, encryption or decryption speed is over 2000 megabyte/second (per core); while decryption of a 1024-bit cryptogram in the above scheme can perhaps run at 4000 per second (per thread of a comparable CPU), thus a throughput of 0.4 megabyte/second, 5000 times slower; that's also moreless the ratio of power usage. That ratio tends to get even worse as security increases. While there are more efficient schemes, it is safe to say that a symmetric scheme is orders of magnitude faster and less power hungry than an asymmetric one, at least for decryption (some asymmetric schemes, including RSA with low public exponent, are considerably faster on the encryption side than they are on the decryption side, and can approach the throughput of some symmetric cryptography).
Size of RSA encrypted content :

RSA encryption (publickey/privatekey) is powerful encryption but its really only meant to encrypt small things like other encryption keys. The maximum size of the amount of data an rsa public key can encrypt is: (key_size/8).floor - 11 i,e It 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. RSA encryption usually is only used for messages that fit into one block.
• 1024-bit RSA key invocation can encrypt a message up to 117 bytes, and results in a 128-byte value
• 2048-bit RSA key invocation can encrypt a message up to 245 bytes
 No. RSA Key Size Max Bytes = [Key length / 8)  - 11 Data Size Key Initialize Time Encrytpion Time Decryption Time 1 1024 bits (1024/8) - 11 = 117 117 bytes 0.359 Secs 0.234 Secs 0.007 Secs 2 2048 bits (2048/8) - 11 = 245 245 bytes 0.734 Secs 0.249 Secs 0.016 Secs 3 3072 bits (3072/8) - 11 = 373 373 bytes 3.262 Secs 0.254 Secs 0.016 Secs 4 4096 bits (4096/8) - 11 = 501 501 bytes 3.984 Secs 0.250 Secs 0.052 Secs 5 5120 bits (5120/8) - 11 = 629 629 bytes 5.584 Secs 0.315 Secs 0.172 Secs 6 6144 bits (6144/8) - 11 = 757 757 bytes 11.265 Secs 0.280 Secs 0.203 Secs 7 7168 bits (7168/8) - 11 = 885 885 bytes 40.832 Secs 0.282 Secs 0.282 Secs 8 8192 bits (8192/8) - 11 = 1013 1013 bytes 42.861 Secs 0.249 Secs 0.384 Secs 9 9216 bits (9216/8) - 11 = 1141 1141 bytes 66.939 Secs 0.243 Secs 0.549 Secs 10 10240 bits (10240/8)-11 = 1269 1269 bytes 93.505 Secs 0.115 Secs 0.688 Secs 11 11264 bits (11264/8)-11 = 1397 1397 bytes 45.006 Secs 0.195 Secs 0.921 Secs 12 12288 bits (12288/8)-11 = 1525 1525 bytes 403.526 Secs 0.250 Secs 1.155 Secs 13 13312 bits (13312/8)-11 = 1653 1653 bytes 216.144 Secs 0.305 Secs 1.476 Secs 14 14336 bits (14336/8)-11 = 1781 1781 bytes 123.481 Secs 0.325 Secs 1.827 Secs 15 15360 bits (15360/8)-11 = 1909 1909 bytes 1601.172 Secs 0.315 Secs 2.232 Secs

RSA Decryption Benchmark in Java
```import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Calendar;
import javax.crypto.Cipher;

public class RSAUtil {
private static PrivateKey privateKey;
private static PublicKey publicKey;

public static PublicKey getPublicKey(String base64PublicKey){
PublicKey publicKey = null;
try{
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return publicKey;
}

public static PrivateKey getPrivateKey(String base64PrivateKey){
PrivateKey privateKey = null;
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes()));
KeyFactory keyFactory = null;
try {
keyFactory = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
privateKey = keyFactory.generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return privateKey;
}

public static byte[] encrypt(String data) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data.getBytes());
}

public static String decrypt(byte[] data)  throws Exception {
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(data));
}

public static String decrypt(String data)  throws Exception {
return decrypt(Base64.getDecoder().decode(data.getBytes()));
}

public static void main(String[] args) {
generateKeyAndRandomData(1024*1, 117);
generateKeyAndRandomData(1024*2, 245);
generateKeyAndRandomData(1024*3, 373);
generateKeyAndRandomData(1024*4, 501);
generateKeyAndRandomData(1024*5, 629);
generateKeyAndRandomData(1024*6, 757);
generateKeyAndRandomData(1024*7, 885);
generateKeyAndRandomData(1024*8, 1013);
generateKeyAndRandomData(1024*9, 1141);
generateKeyAndRandomData(1024*10, 1269);
generateKeyAndRandomData(1024*11, 1397);
generateKeyAndRandomData(1024*12, 1525);
generateKeyAndRandomData(1024*13, 1653);
generateKeyAndRandomData(1024*14, 1781);
generateKeyAndRandomData(1024*15, 1909);
}

private static void generateKeyAndRandomData(int keyInBits, int randomDataSize) {
try {
System.out.println("###################################");
String data = getAlphaNumericString(randomDataSize);
long start = Calendar.getInstance().getTimeInMillis();
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
System.out.println("Key Length : "+keyInBits + " bits/" + (keyInBits / 8) + " bytes, Plain Data Length :"+randomDataSize);
keyGen.initialize(keyInBits);
KeyPair pair = keyGen.generateKeyPair();
privateKey = pair.getPrivate();
publicKey = pair.getPublic();
long end = Calendar.getInstance().getTimeInMillis();
System.out.println("Key Generation Time Taken in MilliSec: " + (end - start));
start = Calendar.getInstance().getTimeInMillis();
String encryptedString = Base64.getEncoder().encodeToString(encrypt(data));
//System.out.println(encryptedString);
end = Calendar.getInstance().getTimeInMillis();
System.out.println("Encryption Time Taken in MilliSec: " + (end - start));
start = Calendar.getInstance().getTimeInMillis();
String decryptedString = decrypt(encryptedString);
//System.out.println(decryptedString);
end = Calendar.getInstance().getTimeInMillis();
System.out.println("Decryption Time Taken in MilliSec: " + (end - start));
} catch (Exception e) {
System.err.println(e.getMessage());
}
}

// function to generate a random string of length n
static String getAlphaNumericString(int n) {
// chose a Character random from this String
String AlphaNumericString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "abcdefghijklmnopqrstuvxyz";
// create StringBuffer size of AlphaNumericString
StringBuilder sb = new StringBuilder(n);
for (int i = 0; i < n; i++) {
// generate a random number between 0 to AlphaNumericString variable length
int index = (int) (AlphaNumericString.length() * Math.random());
// add Character one by one in end of sb
sb.append(AlphaNumericString.charAt(index));
}
return sb.toString();
}
}
```
Output :
```###################################
Key Length : 1024 bits/128 bytes, Plain Data Length :117
Key Generation Time Taken in MilliSec: 443
Encryption Time Taken in MilliSec: 343
Decryption Time Taken in MilliSec: 3

###################################
Key Length : 2048 bits/256 bytes, Plain Data Length :245
Key Generation Time Taken in MilliSec: 494
Encryption Time Taken in MilliSec: 1
Decryption Time Taken in MilliSec: 16

###################################
Key Length : 3072 bits/384 bytes, Plain Data Length :373
Key Generation Time Taken in MilliSec: 486
Encryption Time Taken in MilliSec: 1
Decryption Time Taken in MilliSec: 27

###################################
Key Length : 4096 bits/512 bytes, Plain Data Length :501
Key Generation Time Taken in MilliSec: 2074
Encryption Time Taken in MilliSec: 1
Decryption Time Taken in MilliSec: 52

###################################
Key Length : 5120 bits/640 bytes, Plain Data Length :629
Key Generation Time Taken in MilliSec: 19014
Encryption Time Taken in MilliSec: 2
Decryption Time Taken in MilliSec: 109

###################################
Key Length : 6144 bits/768 bytes, Plain Data Length :757
Key Generation Time Taken in MilliSec: 15109
Encryption Time Taken in MilliSec: 2
Decryption Time Taken in MilliSec: 165

###################################
Key Length : 7168 bits/896 bytes, Plain Data Length :885
Key Generation Time Taken in MilliSec: 108552
Encryption Time Taken in MilliSec: 2
Decryption Time Taken in MilliSec: 256

###################################
Key Length : 8192 bits/1024 bytes, Plain Data Length :1013
Key Generation Time Taken in MilliSec: 71350
Encryption Time Taken in MilliSec: 3
Decryption Time Taken in MilliSec: 372

###################################
Key Length : 9216 bits/1152 bytes, Plain Data Length :1141
Key Generation Time Taken in MilliSec: 34918
Encryption Time Taken in MilliSec: 4
Decryption Time Taken in MilliSec: 527

###################################
Key Length : 10240 bits/1280 bytes, Plain Data Length :1269
Key Generation Time Taken in MilliSec: 65355
Encryption Time Taken in MilliSec: 5
Decryption Time Taken in MilliSec: 704

###################################
Key Length : 11264 bits/1408 bytes, Plain Data Length :1397
Key Generation Time Taken in MilliSec: 522685
Encryption Time Taken in MilliSec: 5
Decryption Time Taken in MilliSec: 930

###################################
Key Length : 12288 bits/1536 bytes, Plain Data Length :1525
Key Generation Time Taken in MilliSec: 30906
Encryption Time Taken in MilliSec: 7
Decryption Time Taken in MilliSec: 1239

###################################
Key Length : 13312 bits/1664 bytes, Plain Data Length :1653
Key Generation Time Taken in MilliSec: 26756
Encryption Time Taken in MilliSec: 7
Decryption Time Taken in MilliSec: 1512

###################################
Key Length : 14336 bits/1792 bytes, Plain Data Length :1781
Key Generation Time Taken in MilliSec: 1839642
Encryption Time Taken in MilliSec: 8
Decryption Time Taken in MilliSec: 1867

###################################
Key Length : 15360 bits/1920 bytes, Plain Data Length :1909
Key Generation Time Taken in MilliSec: 577723
Encryption Time Taken in MilliSec: 10
Decryption Time Taken in MilliSec: 2517
```

What is the maximum length of private and public RSA keys?
• In theory, there is no limit. In practice, there is a limit. Also, limits are usually imposed on the modulus size (n = p*q), and not the public or private key per se.
• For OpenSSL and RSA, your RSA keys are limited to 16K at generation. There's also a limit imposed by OpenSSL's s_client utility used during key exchange. The limit during key exchange is 2K, and it seems artificially low to me. You can side-step the s_client limit by avoiding key transport schemes used during key agreement (i.e., use DH or EDH instead of RSA).
• If you start hitting the limits, then it usually indicates its time to switch to elliptic curves. 16K RSA and 521-bit EC provides about 512-bits of security.
Note : The NIST recommends 2048-bit keys for RSA. An RSA key length of 3072 bits should be used if security is required beyond 2030. NIST key management guidelines further suggest that 15360-bit RSA keys are equivalent in strength to 256-bit symmetric keys.

A larger key increases the maximum number of bytes that we can encrypt at once, and also the security of the encryption. But it has a serious problem in practice:
• With every doubling of the RSA key length, decryption is 6-7 times times slower.
• The key length also affects the speed of encryption, but it's usually the speed of decryption that we're more concerned about because (a) that's the part that takes place on the server, and (b) decryption is much much slower than encryption, because the decryption exponent is huge whereas the encryption exponent is typically small.
• If we use a 4096-bit modulus, it takes around a second of CPU time to decrypt a block of data. Even if you were able to sacrifice this amount of CPU to every log on, it leaves us with the problem that an attacker can effectively burn a second of CPU time on our server by firing some random data at it. With a 1024-bit key length, decryption takes just 25 milliseconds; with suitable restrictions on the rate of login attemps (and thus decryptions) we allow per remote client, protecting against a "CPU burn" attack is more feasible.
In Java, there is a limitation on key size by the JCE Jurisdiction Policy. If you manipulate a private key with bit size which is larger than the limitation, it will throw a InvalidKeyException complaining about Illegal key size.

Maximum Key Length Allowed by JCE
```import java.security.Security;
import java.util.Set;
import javax.crypto.Cipher;

public class CheckKeySize {
public static void main(String[] args) {
try {
Set algorithms = Security.getAlgorithms("Cipher");
for(String algorithm: algorithms) {
System.out.printf("%-30s: %dbit%n", algorithm, Cipher.getMaxAllowedKeyLength(algorithm));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
Output :
```PBEWITHHMACSHA384ANDAES_128   : 2147483647bit
PBEWITHHMACSHA512ANDAES_128   : 2147483647bit
PBEWITHHMACSHA224ANDAES_256   : 2147483647bit
AESWRAP_192                   : 2147483647bit
AESWRAP                       : 2147483647bit
PBEWITHMD5ANDDES              : 2147483647bit
PBEWITHHMACSHA256ANDAES_256   : 2147483647bit
PBEWITHHMACSHA1ANDAES_128     : 2147483647bit
PBEWITHSHA1ANDRC4_128         : 2147483647bit
DESEDEWRAP                    : 2147483647bit
AESWRAP_256                   : 2147483647bit
RC2                           : 2147483647bit
PBEWITHSHA1ANDRC4_40          : 2147483647bit
RSA                           : 2147483647bit
AESWRAP_128                   : 2147483647bit
PBEWITHHMACSHA512ANDAES_256   : 2147483647bit
DESEDE                        : 2147483647bit
BLOWFISH                      : 2147483647bit
ARCFOUR                       : 2147483647bit
AES                           : 2147483647bit
DES                           : 2147483647bit
PBEWITHHMACSHA256ANDAES_128   : 2147483647bit
PBEWITHSHA1ANDDESEDE          : 2147483647bit
PBEWITHSHA1ANDRC2_40          : 2147483647bit
PBEWITHHMACSHA384ANDAES_256   : 2147483647bit
PBEWITHSHA1ANDRC2_128         : 2147483647bit
PBEWITHMD5ANDTRIPLEDES        : 2147483647bit
PBEWITHHMACSHA1ANDAES_256     : 2147483647bit
PBEWITHHMACSHA224ANDAES_128   : 2147483647bit
```

RSA by itself is deterministic , this is why you are getting different result for the same text message.

Why would not use private key for encryption and public key for decryption ?
• Mathematically it will work just fine. "Encrypt" with the private key, "decrypt" with the public key.
• Typically a hash function and padding is involved for signing a message.
• We would completely defeat the purpose of public key cryptography i,e reason why we sign with a private key and not the public key.
• Using the private key for encrypting and the public for decrypting , that’s the process of signing a message/verifying that a message is coming from a particular source.
• Using the public key for encrypting and the private for decrypting , that’s secure communication.
Why does RSA encrypted text(cipher text) give different results for the same text ?
• A secure RSA encryption is implemented with an appropriate padding scheme, which includes some randomness ( PKCS#1 or OAEP or PSS).
• The RSA encryption encrypts message padded with '0's and and a string of random bit. In the process,the random string is "hidden" in the ciphertext by cryptographic hashing and XORing. On decryption, the RSA decryption recovers the random string from the ciphertext and use it to recover message.
• PKCS#1 also describes a newer padding scheme, called PSS, which is more complex but with a stronger security proof.PSS includes a bunch of random bytes, so you will get a distinct signature every time.
Example: (Get a distinct cipher text every time  - Dynamic bunch of random bytes using SecureRandom)
Implicit include bunch of random bytes using SecureRandom:
```Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey)); ```
Explicit include bunch of random bytes using SecureRandom :

```Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey),new SecureRandom());```
The above code will produce distinct cipher text every time , as SecureRandom will add random bytes with plain text and that bytes will be distinct.
Plain Text    : Give me the same result
Cipher Text 1 : JBYqC3FugRnpIsLQydDlCKwPuMrdcNRItqCJjCkfOb7zOS91x2isErk2A7HrPrUKl0rDoHHFZ0IK8PbWW................
Cipher Text 2 : Ff02kTTbsM2KHAb6z8re7s49D2zGw6+aAkuJFGQ87cefsTuVc+6ypFRbfDu1PBx853zl54oDf+/MYB6.................

How RSA encrypted text(cipher text)  will give same result for the same text ?

• As PKCS#1 / PSS includes a bunch of random bytes with the plain text before encryption , which will get a distinct cipher text every time for same input.
• So instead of including a bunch of random bytes , we can include explicitly a fixed bytes of salt to get the same cipher text every time for same input.

Example: (Get a unique cipher text every time  - Static bunch of random bytes using SecureRandom)
Provided explicit fixed bytes into SecureRandom:
```Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey),new SecureRandom("MY-OWN-FIXED-SECURE-SALT".getBytes()));
```
The above code will produce same cipher text every time , as SecureRandom won't add random bytes with plain text and it will be add fixed bytes("MY-OWN-FIXED-SECURE-SALT") every time.
Plain Text    : Give me the same result
Cipher Text 1 : ZjDokVp31oFX+cjaVA2IP0ljtWlNZmzolPn96Q9osbxBJ2P/qCPologKfHHT8lf............................
Cipher Text 2 : ZjDokVp31oFX+cjaVA2IP0ljtWlNZmzolPn96Q9osbxBJ2P/qCPologKfHHT8lf............................

Where RSA Algorithim would be Use :

If we will check the set of algorithms  that cipher suites usually contain include: a key exchange algorithm, a bulk encryption algorithm, and a message authentication code (MAC) algorithm.
Structure of Cipher Suite : Protocol_keyexchange_keyauthentication_WITH_bulkencryption_effectivebits_ciphermode_messageauth, where RSA  will always be found for Key Authentication but not for bulk encryption. Hybrid approach of using RSA (Encrypt the data with a symmetric key like AES, and encrypt that symmetric AES key with Asymmetric  key like RSA)

Step 1 : Generate a symmetric key(AES with 128 bits)

```KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128);
SecretKey secKey = generator.generateKey();
symmetricSecretKey = Base64.encodeBase64String(secKey.getEncoded());
System.out.println("Generated Symmetric Secret Key :" + symmetricSecretKey);```
Step 2 : Encrypt the data with above symmetric key
```String plainText = "JAVA2DEPTH Hybrid Encryption demo...";
aesEncryptionCipher.init(Cipher.ENCRYPT_MODE,new SecretKeySpec(Base64.decodeBase64(symmetricSecretKey), "AES"));
byte[] byteCipherText = aesEncryptionCipher.doFinal(plainText.getBytes());
String encryptedData = Base64.encodeBase64String(byteCipherText);
System.out.println("Encrypted Cipher Text using  Symmetric Secret Key: " + encryptedData);```
Step 3 : Encrypt the symmetric key with RSA 2048 bits
```KeyFactory fact = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec spec = fact.getKeySpec(keyPair.getPrivate(), PKCS8EncodedKeySpec.class);
byte[] packed = spec.getEncoded();
assymetricPrivateKey = Base64.encodeBase64String(packed);
Arrays.fill(packed, (byte) 0);
System.out.println("Generated ASymmetric RSA Private Key : " + assymetricPrivateKey);

String encryptedSymmetricKey = rsaEncryptData(assymetricPublicKey, symmetricSecretKey);
System.out.println("Encrypted Symmetric Secret Key using RSA Private Key :" + encryptedSymmetricKey);```
Step 4 : Send encrypted data (byteCipherText) + encrypted AES Key (encryptedKey)

Step 5 :
Decrypt the encrypted symmetric key with RSA private ke
```String decryptedSymmetricKey = rsaDecryptData(encryptedSymmetricKey, assymetricPrivateKey);
System.out.println("Decrypted Symmetric Secret Key using RSA Public Key :" + encryptedSymmetricKey);```
Step 6 : Decrypt the data with the symmetric key
```Cipher aesDecryptionCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
aesDecryptionCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(decryptedSymmetricKey), "AES"));
byte[] bytePlainText = aesDecryptionCipher.doFinal(Base64.decodeBase64(encryptedData));
String message = new String(bytePlainText);
System.out.println("Decrypted Plain Text using Symmetric Secret Key : " + message);
```
Full Example :
```import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

public class HybridEncryption {
private static String assymetricPublicKey = "";
private static String assymetricPrivateKey = "";
private static String symmetricSecretKey = "";

public static void main(String[] args) throws Exception {
// Sender Side
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128);
SecretKey secKey = generator.generateKey();
symmetricSecretKey = Base64.encodeBase64String(secKey.getEncoded());
System.out.println("Generated Symmetric Secret Key :" + symmetricSecretKey);

String plainText = "JAVA2DEPTH Hybrid Encryption demo...";
aesEncryptionCipher.init(Cipher.ENCRYPT_MODE,new SecretKeySpec(Base64.decodeBase64(symmetricSecretKey), "AES"));
byte[] byteCipherText = aesEncryptionCipher.doFinal(plainText.getBytes());
String encryptedData = Base64.encodeBase64String(byteCipherText);
System.out.println("Encrypted Cipher Text using  Symmetric Secret Key: " + encryptedData);

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();

byte[] keyBytes = keyPair.getPublic().getEncoded();
assymetricPublicKey = Base64.encodeBase64String(keyBytes);
assymetricPublicKey = assymetricPublicKey.replaceAll("\n", "\\\\n");
System.out.println("Generated ASymmetric RSA Public Key :" + assymetricPublicKey);

KeyFactory fact = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec spec = fact.getKeySpec(keyPair.getPrivate(), PKCS8EncodedKeySpec.class);
byte[] packed = spec.getEncoded();
assymetricPrivateKey = Base64.encodeBase64String(packed);
Arrays.fill(packed, (byte) 0);
System.out.println("Generated ASymmetric RSA Private Key : " + assymetricPrivateKey);

String encryptedSymmetricKey = rsaEncryptData(assymetricPublicKey, symmetricSecretKey);
System.out.println("Encrypted Symmetric Secret Key using RSA Private Key :" + encryptedSymmetricKey);

// Reciever Side
System.out.println("Decrypted Symmetric Secret Key using RSA Public Key :" + encryptedSymmetricKey);

aesDecryptionCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(decryptedSymmetricKey), "AES"));
byte[] bytePlainText = aesDecryptionCipher.doFinal(Base64.decodeBase64(encryptedData));
String message = new String(bytePlainText);
System.out.println("Decrypted Plain Text using Symmetric Secret Key : " + message);
}

private static String rsaEncryptData(String publicKey, String message) {
byte[] cipherBytes = null;
String encryptData64Encoded = "";
try {
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
byte[] eMessageBytes = message.getBytes();
cipherBytes = cipher.doFinal(eMessageBytes);
encryptData64Encoded = new String(Base64.encodeBase64(cipherBytes));
} catch (Exception e) {
e.printStackTrace();
}
return encryptData64Encoded;
}

private static String rsaDecryptData(String encryptData, String privatekey) {
String dectyptedText = "";
try {
byte[] dataEncoded = Base64.decodeBase64(encryptData);
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privatekey));
dectyptedText = new String(cipher.doFinal(dataEncoded));
} catch (Exception e) {
e.printStackTrace();
}
return dectyptedText;
}

private static PrivateKey getPrivateKey(String privatekey) {
PrivateKey privateKeyObj = null;
try {
byte[] clear = Base64.decodeBase64(privatekey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(clear);
KeyFactory fact = KeyFactory.getInstance("RSA");
privateKeyObj = fact.generatePrivate(keySpec);
Arrays.fill(clear, (byte) 0);
} catch (Exception e) {
e.printStackTrace();
}
return privateKeyObj;
}

private static PublicKey getPublicKey(String stored) {
PublicKey publicObj = null;
try {
byte[] data = Base64.decodeBase64(stored);
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
publicObj = fact.generatePublic(spec);
} catch (Exception e) {
e.printStackTrace();
}
return publicObj;
}
}
```
Output :
```Generated Symmetric Secret Key :yDv3bqfbzS1hiuCE9it/nA==
Encrypted Cipher Text using  Symmetric Secret Key: w149Rjf6b7WzVoYzvqCJFP19vy6gqenZtY2YwnO0qgrs4Z61BWEzHyhHE3Sei+UI
Generated ASymmetric RSA Public Key :MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgP+CGTQW01AmDSi+kcPX/9A/i7+opvjgyvcI47HbUd80622+umVWJIVrksmAuc+i2d8kG/rbVlBep90LPXpttef6gI+Ubqr/6AzfWEFdKdXFC3ELff2rR2zmJD3e0zzuwHoUvHL/lFFAyDLyonj0fDlQ/0jJaHJd6gpb0KOCCedewhBNamOzFusbp8TpgwV60tECykItp5YCnC7xw01hcni9dc4dzTb4IQ7NSa/ZNzoSnZStXo6X7qpqQ3FNUF09HPn4gh0c8XNyA4uyC28qDyVki51VUSiXeZ1oXOJ5YTMbQaIYAFcfY1ZKyb3irDx7gdA8n2LdpuMAuD3YAjlQ4QIDAQAB