AsymmetricCrypto

Introduction

For asymmetric encryption, the most commonly used algorithms are RSA and DSA. In Hutool, the AsymmetricCrypto object is used to handle encryption and decryption.

Asymmetric encryption involves two concepts: public key and private key. The private key is kept secret and not shared with others, while the public key is made public. Depending on the application, we can choose to use different keys for encryption:

  1. Signing: Encrypting with a private key and decrypting with a public key. This is used to allow all owners of the public key to verify the identity of the owner of the private key and to prevent the content published by the owner of the private key from being tampered with, but it does not guarantee that the content will not be obtained by others.

  2. Encryption: Encrypting with a public key and decrypting with a private key. This is used to publish information to the owner of the public key. The information may be tampered with by others, but it cannot be obtained by others.

Hutool encapsulates the JDK’s asymmetric encryption algorithms. For more details, see https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator:

  • RSA
  • RSA_ECB_PKCS1 (RSA/ECB/PKCS1Padding)
  • RSA_None (RSA/None/NoPadding)
  • ECIES (requires Bouncy Castle library)

Usage

In asymmetric encryption, we can obtain an encryption and decryption device by passing different algorithm enumerations to the AsymmetricCrypto(AsymmetricAlgorithm algorithm) constructor.

For convenience, we have created a separate object RSA for the most commonly used RSA algorithm.

Basic Usage

Let’s take RSA as an example to introduce how to encrypt and decrypt using RSA. When constructing an RSA object, you can pass in a public key or private key. If you use the no-argument constructor, Hutool will automatically generate a random key pair of public and private keys:

RSA rsa = new RSA();

// Get private key
rsa.getPrivateKey();
rsa.getPrivateKeyBase64();
// Get public key
rsa.getPublicKey();
rsa.getPublicKeyBase64();

// Encrypt with public key and decrypt with private key
byte[] encrypt = rsa.encrypt(StrUtil.bytes("I am a test aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);

// Junit unit test
// Assert.assertEquals("I am a test aaaa", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));

// Encrypt with private key and decrypt with public key
byte[] encrypt2 = rsa.encrypt(StrUtil.bytes("I am a test aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey);

// Junit unit test
// Assert.assertEquals("I am a test aaaa", StrUtil.str(decrypt2, CharsetUtil.CHARSET_UTF_8));

Encryption and decryption can be completely separated. For the RSA object, if only the public key or private key is used, the other parameter can be null.

Self-generated Key Pair

Sometimes we want to generate a key pair ourselves:

KeyPair pair = SecureUtil.generateKeyPair("RSA");
pair.getPrivate();
pair.getPublic();

The self-generated key pair is in the form of byte[], which can be converted to Base64 using the Base64.encode method for easy storage as text. Of course, if you use the RSA object, you can also use encryptStr and decryptStr to encrypt and decrypt strings.

Case

Case 1:

How to decrypt ciphertext with known private key?

String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIL7pbQ+5KKGYRhw7jE31hmA"
		+ "f8Q60ybd+xZuRmuO5kOFBRqXGxKTQ9TfQI+aMW+0lw/kibKzaD/EKV91107xE384qOy6IcuBfaR5lv39OcoqNZ"
		+ "5l+Dah5ABGnVkBP9fKOFhPgghBknTRo0/rZFGI6Q1UHXb+4atP++LNFlDymJcPAgMBAAECgYBammGb1alndta"
		+ "xBmTtLLdveoBmp14p04D8mhkiC33iFKBcLUvvxGg2Vpuc+cbagyu/NZG+R/WDrlgEDUp6861M5BeFN0L9O4hz"
		+ "GAEn8xyTE96f8sh4VlRmBOvVdwZqRO+ilkOM96+KL88A9RKdp8V2tna7TM6oI3LHDyf/JBoXaQJBAMcVN7fKlYP"
		+ "Skzfh/yZzW2fmC0ZNg/qaW8Oa/wfDxlWjgnS0p/EKWZ8BxjR/d199L3i/KMaGdfpaWbYZLvYENqUCQQCobjsuCW"
		+ "nlZhcWajjzpsSuy8/bICVEpUax1fUZ58Mq69CQXfaZemD9Ar4omzuEAAs2/uee3kt3AvCBaeq05NyjAkBme8SwB0iK"
		+ "kLcaeGuJlq7CQIkjSrobIqUEf+CzVZPe+AorG+isS+Cw2w/2bHu+G0p5xSYvdH59P0+ZT0N+f9LFAkA6v3Ae56OrI"
		+ "wfMhrJksfeKbIaMjNLS9b8JynIaXg9iCiyOHmgkMl5gAbPoH/ULXqSKwzBw5mJ2GW1gBlyaSfV3AkA/RJC+adIjsRGg"
		+ "JOkiRjSmPpGv3FOhl9fsBPjupZBEIuoMWOC8GXK/73DHxwmfNmN7C9+sIi4RBcjEeQ5F5FHZ";

RSA rsa = new RSA(PRIVATE_KEY, null);

String a = "2707F9FD4288CEF302C972058712F24A5F3EC62C5A14AD2FC59DAB93503AA0FA17113A020EE4EA35EB53F"
		+ "75F36564BA1DABAA20F3B90FD39315C30E68FE8A1803B36C29029B23EB612C06ACF3A34BE815074F5EB5AA3A"
		+ "C0C8832EC42DA725B4E1C38EF4EA1B85904F8B10B2D62EA782B813229F9090E6F7394E42E6F44494BB8";

byte[] aByte = HexUtil.decodeHex(a);
byte[] decrypt = rsa.decrypt(aByte, KeyType.PrivateKey);

//Junit unit test
//Assert.assertEquals("虎头闯杭州,多抬头看天,切勿只管种地", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));

Other Algorithms

ECIES

ECIES stands for Elliptic Curve Integrated Encryption Scheme.

Hutool can support the ECIES algorithm with the help of the Bouncy Castle library:

We first need to introduce the Bouncy Castle library:

<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcprov-jdk15to18</artifactId>
  <version>1.66</version>
</dependency>
final ECIES ecies = new ECIES();
String textBase = "I am a particularly long test";
StringBuilder text = new StringBuilder();
for (int i = 0; i < 10; i++) {
 text.append(textBase);
}

// Encrypt with public key and decrypt with private key
String encryptStr = ecies.encryptBase64(text.toString(), KeyType.PublicKey);
String decryptStr = StrUtil.utf8Str(ecies.decrypt(encryptStr, KeyType.PrivateKey));