A, cause
Recently, when I was working, suddenly a transaction was extremely slow, the call time was as high as 40wms, which seriously affected the productivity of the whole cluster. Knowing this, I began to analyze the entire transaction, and eventually found that it was the RSA asymmetric encryption algorithm pot.
RSA is an asymmetric encryption algorithm. An important feature of RSA is that when data is transmitted over the network, the key used to encrypt data does not need to be transmitted with the data. As a result, this reduces the possibility of key leakage. RSA is also useful when the encryptor is not allowed to decrypt data. The encryptor uses one key, called the public key, and the decryptor uses another key, called the private key. The private key needs to be kept private.
RSA is very secure, but its efficiency is very low. The length of the secret key itself is 1024 bits. With the development of large number parsing technology, the length of the secret key will increase further. At the same time, because RSA generates random number decryption will lock the thread, which will cause thread congestion. Of course, because there are not many transactions using RSA encryption and decryption, congestion estimation is a special case.
All in all, RSA is an inefficient encryption method that is not appropriate for encrypting large amounts of data, even in areas such as signatures, and should be used as sparingly as possible, otherwise performance will suffer.
However, there were limitations when my former colleagues wrote this code, using RSA encryption in a place where the security requirements were not particularly high, while not considering the number of calls to this module.
Therefore, after testing and thinking, finally decided to upgrade the encryption algorithm, using SM4 algorithm.
Second, SM4 national secret algorithm introduction
SMS4 algorithm is an encryption algorithm widely used in WAPI wireless network standard in China. It is a 32-round iterative non-equilibrium Feistel structure block encryption algorithm, and its key length and block length are both 128. The algorithm used in the encryption and decryption process of SMS4 algorithm is exactly the same, the only difference is that the decryption key of this algorithm is obtained by the inverse transformation of its encryption key.
When I was going to study SM4 algorithm, the China Internet Network Information Center did not provide the standard documents of SM4 algorithm, so I had to go to the Internet to consult the materials. Thanks for the article with you learn the domestic encryption algorithm SM4 Java implementation scheme of this article.
1. Principle of SM4 algorithm
chart:
We can see that the plaintext plus 128bit secret key, through many rounds of transformation, the final output is the reverse order of encryption, decryption is only the use of the key round of reverse order.
2. Application scenarios of SM4 encryption algorithms
SM4 is often used to encrypt data transmission in government systems. We can use this algorithm for transmitting information from the front end to the back end, or for calls between different modules in distributed scenarios. The parameter data is encrypted, and then the background decrypts the encrypted data and stores it in the database to ensure that the data is not leaked during transmission.
3, SM4 algorithm Java implementation
Now I will simply implement the SM4 algorithm encryption and decryption function
The first is a number of constant values, including the encoding, algorithm name, secret key length, etc., if can be written as parameter values.
private static final String ENCODING = "UTF-8";
public static final String ALGORIGTHM_NAME = "SM4";
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
public static final int DEFAULT_KEY_SIZE = 128;
Copy the code
The next is to generate ECB encryption and decrypt, encryption and decrypt is through the secret key of the background, because the secret key generation algorithm is consistent, and is used as a JAR package, so can be written together.
The purpose of this function is to use ECB mode, which is one of the most basic working modes of block ciphers. The second parameter, data, is used to determine whether to encrypt or decrypt the data.
Here’s a picture of some of the patterns.
/** * @description: generate ECB secret key */ private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance(algorithmName,BouncyCastleProvider.PROVIDER_NAME); Key sm4Key = new SecretKeySpec(key, ALGORIGTHM_NAME); cipher.init(mode, sm4Key); return cipher; } /** * @description: Automatically generates a key. */ public static byte[] generateKey() throws Exception {return generateKey(DEFAULT_KEY_SIZE); } public static byte[] generateKey(int keySize) throws Exception { KeyGenerator kg = KeyGenerator.getInstance(ALGORIGTHM_NAME, BouncyCastleProvider.PROVIDER_NAME); kg.init(keySize, new SecureRandom()); return kg.generateKey().getEncoded(); }Copy the code
Concrete encryption decryption class implementation is as follows:
/** * @description: encrypt */ public static String encryptEcb(String hexKey, String paramStr, String charset) throws Exception { String cipherText = ""; if (null ! = paramStr && !" ".equals(paramStr)) { byte[] keyData = ByteUtils.fromHexString(hexKey); charset = charset.trim(); if (charset.length() <= 0) { charset = ENCODING; } byte[] srcData = paramStr.getBytes(charset); byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData); cipherText = ByteUtils.toHexString(cipherArray); } return cipherText; } /** * @description: encryption mode ECB */ public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception { Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key); byte[] bs = cipher.doFinal(data); return bs; /** * @description :sm4 decrypt */ public static String decryptEcb(String hexKey, String cipherText, String decryptEcb) String charset) throws Exception { String decryptStr = ""; byte[] keyData = ByteUtils.fromHexString(hexKey); byte[] cipherData = ByteUtils.fromHexString(cipherText); byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData); charset = charset.trim(); if (charset.length() <= 0) { charset = ENCODING; } decryptStr = new String(srcData, charset); return decryptStr; } /** * @description: ECB decrypt */ public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception { Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key); return cipher.doFinal(cipherText); }}Copy the code
Of course, when you’re testing, you can also add validation classes
/** * @description: Password verification */ public static Boolean verifyEcb(String hexKey,String cipherText,String paramStr) throws Exception { boolean flag = false; byte[] keyData = ByteUtils.fromHexString(hexKey); byte[] cipherData = ByteUtils.fromHexString(cipherText); byte[] decryptData = decrypt_Ecb_Padding(keyData,cipherData); byte[] srcData = paramStr.getBytes(ENCODING); flag = Arrays.equals(decryptData,srcData); return flag; }Copy the code
Finally, add a simple test class, and you’re done. Here, to verify concurrency, use a thread pool, and you can try it yourself.
/** * @description: test class */ public static void main(String[] args) {ThreadPoolExecutor Executor = new ThreadPoolExecutor (20100, 10, TimeUnit. SECONDS, new LinkedBlockingQueue < Runnable > ()); Map map=new HashMap<>(); for (int i=0; i<1000; i++){ int finalI = i; executor.submit(()->{ System.out.println(Thread.currentThread().getName()); int number = new Random().nextInt(100); try { long start,end; start = System.currentTimeMillis(); String json = "{\"name\":\"color\",\"sex\":\"man\"}"+number; / / custom 32-bit hexadecimal key String key = "888368581322491 ace9q79348a2757d1"; String cipher = Sm4Utils.encryptEcb(key, json,ENCODING); System.out.println(" after encryption :"+cipher); Thread.sleep(100); json = Sm4Utils.decryptEcb(key, cipher,ENCODING); System. The out. Println (" revealed: "+ json); System.out.println(Sm4Utils.verifyEcb(key, cipher, json)); System.out.println(" password verification :"+json); end = System.currentTimeMillis(); System.out.println(finalI+"Run Time:" + (end - start) + "(ms)"); map.put(finalI,json); } catch (Exception e) { e.printStackTrace(); }}); }}Copy the code
Some screenshots of the results are shown below, and you can try them out for yourself
Sometimes WHEN I use the JAR package may make mistakes, I incidentally paste the JAR package out for you to use
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
Copy the code
Students in need can add my public account, the latest articles in the future are in the first time, you can also ask me for mind map