1. The encrypted system should not have the decryption function, otherwise RSA may not be suitable

The public key is encrypted and the private key is decrypted. The encrypted system and the decryption system are deployed separately, and the encrypted system should not have the decryption function at the same time, so that even if a hacker breaks into the encryption system, he only gets a pile of unbreakable ciphertext data. Otherwise, you need to consider whether RSA is necessary for your scenario.

2. Modify the length of the generated key to adjust the ciphertext length

The length of the generated ciphertext is equal to the length of the key. The larger the key length is, the longer the ciphertext is generated, the slower the encryption speed is, and the harder the ciphertext is to be broken. The famous law of “safety and efficiency is always a double-edged sword” is on full display here. We must make a balanced choice between “security” and “encryption and decryption efficiency” by defining the length of the key.

3. The length of the generated ciphertext has nothing to do with the length of the plaintext, but the plaintext length cannot exceed the length of the key

The length of ciphertext generated by RSA is always fixed, regardless of the plaintext length. However, the plaintext length cannot exceed the key length. For example, Java’s default RSA encryption implementation does not allow plaintext length to exceed the key length minus 11(in bytes). That is to say, if we define key (. We can through the Java security. KeyPairGenerator. The initialize (int keysize) to define the key length) length of 1024 (unit, is a bit), The generated key length is 1024 bits / 8 bits/byte = 128 bytes, so the plaintext length that we need to encrypt cannot exceed 128 bytes – 11 bytes = 117 bytes. That is to say, we can biggest 117 bytes to encrypt plaintext, or cause problems (behind such as javax.mail. Crypto. IllegalBlockSizeException: Data must not be longer than 53 bytes. The maximum length of RSA plaintext supported by the ENCRYPTION algorithm provided by BC is that of the key.

Byte [].toString() returns the memory address instead of converting the actual contents of an array to a String

Beware of the toString trap: Instead of returning the contents of an array, the toString() method in Java returns an indication of the type of element the array stores and its location in memory. Most people fall into this trap without realizing it, including some who have been writing about Java for years. Take the code In this blog post, How To Convert Byte[] Array To String In Java

public class TestByte
{    
	public static void main(String[] argv) {
 
		    String example = "This is an example";
		    byte[] bytes = example.getBytes();
 
		    System.out.println("Text : " + example);
		    System.out.println("Text [Byte Format] : " + bytes);
		    System.out.println("Text [Byte Format] : " + bytes.toString());
 
		    String s = new String(bytes);
		    System.out.println("Text Decryted : "+ s); }}Copy the code

Text: This is an example Text [Byte Format] : [B@187aeca Text [Byte Format] : [B@187aeca Text Decryted: Final byte[] cipherText = encrypt(originalText, publicKey); System.out.println(“Encrypted: ” +cipherText.toString());

Output: [B@4c3a8ea3 this output is an indication of the location of the byte array in memory, rather than the contents of the string to which the byte array is converted. If we persist the key as byte[].toString() or transfer it json with some other string, As a key to unlock the person will just get a bunch of meaningless characters, are likely to be met when he decoding “javax.mail. Crypto. BadPaddingException” exception.

5. Strings are used to hold text information and byte arrays are used to hold binary data

Java.lang. String holds plaintext and byte arrays hold binary ciphertext, and there should be no conversions between java.lang.String and byte[]. If you do have to use java.lang.String to hold binary data, The safest way is to use Base64 (recommend Apache Commons – codec library org.apache.com mons. Codec. The binary, Base64) :

      // use String to hold cipher binary data
      Base64 base64 = new Base64(); 
      String cipherTextBase64 = base64.encodeToString(cipherText);
      
      // get cipher binary data back from String
      byte[] cipherTextArray = base64.decode(cipherTextBase64);
Copy the code

6. If the ciphertext generated is inconsistent each time, the encryption algorithm you choose is secure

A good encryption must generate different ciphertext each time, even if your plaintext is the same and you use the same public key each time. Because it hides plaintext information more securely. The default Implementation of RSA in Java is “RSA/None/PKCS1Padding”(for example Cipher Cipher = cipher.getInstance (“RSA”); Bouncy Castle’s default RSA implementation is “RSA/None/NoPadding”. Why is it that Java’s default RSA implementation does not generate the same ciphertext every time, even though it uses the same plaintext and the same public key every time? This is because RSA’s PKCS #1 padding scheme randomizes the plaintext information before encrypting it. You can use the following methods to make the same plaintext and the same public key generate the same ciphertext each time, but you must be aware of the cost of doing so. For example, you might use RSA to encrypt transmissions, but because your same plaintext generates the same ciphertext each time, an attacker can tell when the same message was sent.

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance("RSA/None/NoPadding"."BC");
Copy the code

7. You can adjust the algorithm provider to reduce the ciphertext length

Java default RSA implementation RSA/None/PKCS1Padding “require minimum for 512 – bit key length (or you will quote Java. Security. InvalidParameterException: RSA keys must be at least 512 bits long. RSA keys must be at least 512 bits long. If you are too big, you can reduce the ciphertext length by adjusting the algorithm provider:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"."BC");
keyGen.initialize(128);
Copy the code

The resulting ciphertext length is 128 bits (16 bytes). But before you do that, please review point 2 of this article.

8. Ciphers are stateful and thread-unsafe

Javax.crypto.Cipher is statically, do not treat Cipher as a static variable unless your program is single-threaded, which means you can ensure that only one thread is calling the Cipher at any one time. Otherwise you may face Java. Like the author lang. ArrayIndexOutOfBoundsException: too much data for RSA abnormal block. When encountering this exception, you need to check whether the plain text (or Cipher text to be decrypted) for the Cipher text is too long. To rule out clear text (or ciphertext) being too long, you need to consider whether your Cipher threads are unsafe.

Postscript Although RSA Encryption Example has some conceptual misconceptions, I still think it’s a good entry point. Combined with the contents listed in this paper, the author made some adjustments to its code for reference:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
 
import javax.crypto.Cipher;
 
import org.apache.commons.codec.binary.Base64;
 
/ * * *@author JavaDigest
 * 
 */
public class EncryptionUtil {
 
	/** * String to hold name of the encryption algorithm. */
	public static final String ALGORITHM = "RSA";
 
	/** * String to hold name of the encryption padding. */
	public static final String PADDING = "RSA/NONE/NoPadding";
 
	/** * String to hold name of the security provider. */
	public static final String PROVIDER = "BC";
 
	/** * String to hold the name of the private key file. */
	public static final String PRIVATE_KEY_FILE = "e:/defonds/work/20150116/private.key";
 
	/** * String to hold name of the public key file. */
	public static final String PUBLIC_KEY_FILE = "e:/defonds/work/20150116/public.key";
 
	/**
	 * Generate key which contains a pair of private and public key using 1024
	 * bytes. Store the set of keys in Prvate.key and Public.key files.
	 * 
	 * @throws NoSuchAlgorithmException
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	public static void generateKey(a) {
		try {
 
			Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
			final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(
					ALGORITHM, PROVIDER);
			keyGen.initialize(256);
			final KeyPair key = keyGen.generateKeyPair();
 
			File privateKeyFile = new File(PRIVATE_KEY_FILE);
			File publicKeyFile = new File(PUBLIC_KEY_FILE);
 
			// Create files to store public and private key
			if(privateKeyFile.getParentFile() ! =null) {
				privateKeyFile.getParentFile().mkdirs();
			}
			privateKeyFile.createNewFile();
 
			if(publicKeyFile.getParentFile() ! =null) {
				publicKeyFile.getParentFile().mkdirs();
			}
			publicKeyFile.createNewFile();
 
			// Saving the Public key in a file
			ObjectOutputStream publicKeyOS = new ObjectOutputStream(
					new FileOutputStream(publicKeyFile));
			publicKeyOS.writeObject(key.getPublic());
			publicKeyOS.close();
 
			// Saving the Private key in a file
			ObjectOutputStream privateKeyOS = new ObjectOutputStream(
					new FileOutputStream(privateKeyFile));
			privateKeyOS.writeObject(key.getPrivate());
			privateKeyOS.close();
		} catch(Exception e) { e.printStackTrace(); }}/**
	 * The method checks if the pair of public and private key has been
	 * generated.
	 * 
	 * @return flag indicating if the pair of keys were generated.
	 */
	public static boolean areKeysPresent(a) {
 
		File privateKey = new File(PRIVATE_KEY_FILE);
		File publicKey = new File(PUBLIC_KEY_FILE);
 
		if (privateKey.exists() && publicKey.exists()) {
			return true;
		}
		return false;
	}
 
	/**
	 * Encrypt the plain text using public key.
	 * 
	 * @param text
	 *            : original plain text
	 * @param key
	 *            :The public key
	 * @return Encrypted text
	 * @throws java.lang.Exception
	 */
	public static byte[] encrypt(String text, PublicKey key) {
		byte[] cipherText = null;
		try {
			// get an RSA cipher object and print the provider
			Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
			final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
			
			// encrypt the plain text using the public key
			cipher.init(Cipher.ENCRYPT_MODE, key);
			cipherText = cipher.doFinal(text.getBytes());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return cipherText;
	}
 
	/**
	 * Decrypt text using private key.
	 * 
	 * @param text
	 *            :encrypted text
	 * @param key
	 *            :The private key
	 * @return plain text
	 * @throws java.lang.Exception
	 */
	public static String decrypt(byte[] text, PrivateKey key) {
		byte[] dectyptedText = null;
		try {
			// get an RSA cipher object and print the provider
			Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
			final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
 
			// decrypt the text using the private key
			cipher.init(Cipher.DECRYPT_MODE, key);
			dectyptedText = cipher.doFinal(text);
 
		} catch (Exception ex) {
			ex.printStackTrace();
		}
 
		return new String(dectyptedText);
	}
 
	/** * Test the EncryptionUtil */
	public static void main(String[] args) {
 
		try {
 
			// Check if the pair of keys are present else generate those.
			if(! areKeysPresent()) {// Method generates a pair of keys using the RSA algorithm and
				// stores it
				// in their respective files
				generateKey();
			}
 
			final String originalText = "12345678901234567890123456789012";
			ObjectInputStream inputStream = null;
 
			// Encrypt the string using the public key
			inputStream = new ObjectInputStream(new FileInputStream(
					PUBLIC_KEY_FILE));
			final PublicKey publicKey = (PublicKey) inputStream.readObject();
			final byte[] cipherText = encrypt(originalText, publicKey);
 
			// use String to hold cipher binary data
			Base64 base64 = new Base64();
			String cipherTextBase64 = base64.encodeToString(cipherText);
 
			// get cipher binary data back from String
			byte[] cipherTextArray = base64.decode(cipherTextBase64);
 
			// Decrypt the cipher text using the private key.
			inputStream = new ObjectInputStream(new FileInputStream(
					PRIVATE_KEY_FILE));
			final PrivateKey privateKey = (PrivateKey) inputStream.readObject();
			final String plainText = decrypt(cipherTextArray, privateKey);
 
			// Printing the Original, Encrypted and Decrypted Text
			System.out.println("Original=" + originalText);
			System.out.println("Encrypted=" + cipherTextBase64);
			System.out.println("Decrypted=" + plainText);
 
		} catch(Exception e) { e.printStackTrace(); }}}Copy the code

The length of the key is 256 bits, that is to say, the length of the ciphertext generated is 32 bytes. The plaintext with the maximum length of 32 bytes can be encrypted. Because nopadding is used, for the same key and plaintext, This article always generates the same ciphertext; Then use the generated public key to encrypt the plaintext information you provide, generate 32 bytes of binary plaintext, and then use Base64 to convert the binary ciphertext into a string to save; It then shows how to convert Base64 strings back to binary ciphertext; Finally, the binary ciphertext is converted into plain text before encryption. The output of the above program is as follows: Original=12345678901234567890123456789012 Encrypted=GTyX3nLO9vseMJ+RB/dNrZp9XEHCzFkHpgtaZKa8aCc= Decrypted = 12345678901234567890123456789012 resources www.bouncycastle.org/wiki/displa… Stackoverflow.com/questions/1… www.experts-exchange.com/Security/En… Stackoverflow.com/questions/1…

Original post: defonds.blog.csdn.net/article/det…