Three steps to implement Java’s SM2 front-end encryption and back-end decryption

Qin medicine is like poison, and there is no cure.

Without further ado, let’s first introduce the js file download link and jsp front-end code that need to be used.

Step 1: Download two necessary js files – crypto-js.js, sm2.js.

Their download links are as follows ↓ (This webpage will be very stuck if you don’t use magic to access the Internet. After all, github, it is recommended to reload it several times if it is stuck. It took me almost 8 times to get in):

https://github.com/Saberization/SM2

Enter the webpage, download and take the two js on the ↓ picture below:

Then, put these two js in the directory where static resources are stored. I put the sm2 directory in static (the sm2 directory was created by me)

Later, introduce these two js files in the of jsp

<!--jsp introduces sm2 to realize front-end encryption-->
<script type="text/javascript" src="${pageContext.request.contextPath}/static/sm2/crypto-js.js" charset="utf-8"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/sm2/sm2.js" charset="utf-8"></script></pre >
<h6><strong>Second step:</strong> Front-end jsp code implementation, the code below is written in <script type="text/javascript"></script> on the jsp page. </h6>
<p>The function is that when the ↓ function below is called, the value you pass in will be encrypted and returned.</p>
<p><strong>Note: The generation method of [the public key you generated] can be found in the SM2Utils.java tool class at the end of the article. It is a main method, one-click generation, simple and crude</strong></p>
<pre><!--sm2 encryption function-->
function sm2EncryptPwd(data) {
<!--sm2 public key-->
var publicKey = 'The public key you generated';
return sm2Encrypt(data, publicKey, 1);
}

At this point, front-end encryption ends and the back-end decryption step enters

The jar package required for the backend is: bcprov-jdk15on-1.68.jar

Attention! ! ! The jar package must be version 1.60 or above, otherwise it will lack the necessary conditions for decryption! ! ! And when I used it, the local environment was JDK1.8. I have not tried it in other environments. It is recommended that if there is any difference, check it first.

<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcprov-jdk15on</artifactId>
  <version>1.68</version>
</dependency>
Step 3: Back-end code implementation.

After importing the jar package, it is the necessary tool class entity class: SM2Utils.java

You can decrypt using the encryption method of the tool class itself, or you can use the getJieMiJieGuo() method I wrote in the tool class to decrypt.

I personally recommend using what I wrote, because it is simpler to operate. You only need to pass in the encrypted string and the decrypted data can be returned.

Among them, [the public key you generated] and [the private key you generated] can be generated through the methods in the tool class. Just copy and paste them in after generation.

Note! The public keys of the backend and frontend must be consistent! Because the public key and private key are used in pairs!

package com.test.common.utils;

import org.apache.commons.net.util.Base64;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;

public class SM2Utils {

    private static final String publicKey = "The public key you generated";
    private static final String privateKey = "You generate a private key";


    /**
     * Decrypt and obtain the decrypted string
     *
     * @return Qin Wujie
     */
    public static String getJieMiJieGuo(String cipherData) {
        byte[] cipherDataByte = Hex.decode(cipherData);
        BigInteger privateKeyD = new BigInteger(privateKey, 16);
        X9ECParameters sm2ECParameters1 = GMNamedCurves.getByName("sm2p256v1");
        ECDomainParameters domainParameters1 = new ECDomainParameters(sm2ECParameters1.getCurve(), sm2ECParameters1.getG(), sm2ECParameters1.getN());
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters1);

        //Use the private key to decrypt, SM2Engine.Mode.C1C3C2 must be added, otherwise the error invalid cipher text will be reported
        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        sm2Engine.init(false, privateKeyParameters);

        //processBlock gets the Base64 format, remember to decode it
        byte[] arrayOfBytes = null;
        try {
            arrayOfBytes = Base64.decodeBase64(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        //Get plaintext: SM2 Encryption Test
        return new String(arrayOfBytes);
    }


    /**
     * @Description Generate key pair
     * @Author msx
     * @return KeyPair
     */
    public static KeyPair createECKeyPair() {
        //Create parameter specifications generated by EC parameters using standard names
        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");

        // Get an elliptic curve type key pair generator
        final KeyPairGenerator kpg;
        try {
            kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
            // Initialize the key generator using the SM2 algorithm domain parameter set (defaults to using the provider's implementation of SecureRandom installed with the highest priority as the random source)
            // kpg.initialize(sm2Spec);

            // Initialize the key generator using SM2's algorithm domain parameter set and the specified random source
            kpg.initialize(sm2Spec, new SecureRandom());

            // Generate a key pair through the key generator
            return kpg.generateKeyPair();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @Description public key encryption
     * @Author msx
     * @param publicKeyHex SM2 hexadecimal public key
     * @param data plain text data
     * @return String
     */
    public static String encrypt1(String publicKeyHex, String data) {
        return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1);
    }

    /**
     * @Description public key encryption
     * @Author msx
     * @param data plain text data
     * @return String
     */
    public static String encrypt(String data) {
        return encrypt(getECPublicKeyByPublicKeyHex(publicKey), data, 1);
    }

    /**
     * @Description public key encryption
     * @Author msx
     * @param publicKey SM2 public key
     * @param data plain text data
     * @param modeType encryption mode
     * @return String
     */
    public static String encrypt(BCECPublicKey publicKey, String data, int modeType) {
        //encryption mode
        SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
        if (modeType != 1) {
            mode = SM2Engine.Mode.C1C2C3;
        }
        //Get the basic domain parameters of the public key through the public key object.
        ECParameterSpec ecParameterSpec = publicKey.getParameters();
        ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                ecParameterSpec.getG(), ecParameterSpec.getN());
        //Create a public key parameter object through the public key value and basic public key parameters
        ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);
        //Instantiate the SM2 public key encryption engine according to the encryption mode
        SM2Engine sm2Engine = new SM2Engine(mode);
        //Initialize encryption engine
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
        byte[] arrayOfBytes = null;
        try {
            //Convert the plaintext string into a byte string with the specified encoding
            byte[] in = data.getBytes("utf-8");
            //Serial encryption of bytes through the encryption engine
            arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
        } catch (Exception e) {
            System.out.println("Exception occurred during SM2 encryption:" + e.getMessage());
            e.printStackTrace();
        }
        //Convert the encrypted byte string to a hexadecimal string
        return Hex.toHexString(arrayOfBytes);
    }

    /**
     * @Description Private key decryption
     * @Author msx
     * @param privateKeyHex SM2 hexadecimal private key
     * @param cipherData ciphertext data
     * @return String
     */
    public static String decrypt1(String privateKeyHex, String cipherData) {
        return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1);
    }

    /**
     * @Description Private key decryption
     * @Author msx
     * @param s
     * @param cipherData ciphertext data
     * @return String
     */
    public static String decrypt(String s, String cipherData) {
        return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKey), cipherData, 1);
    }

    /**
     * @Description Private key decryption
     * @Author msx
     * @param privateKey SM private key
     * @param cipherData ciphertext data
     * @param modeType decryption mode
     * @return
     */
    public static String decrypt(BCECPrivateKey privateKey, String cipherData, int modeType) {
        //decryption mode
        SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
        if (modeType != 1) {
            mode = SM2Engine.Mode.C1C2C3;
        }
        //Convert the hexadecimal string ciphertext into a byte array (needs to be consistent with encryption, encryption is: the encrypted byte array is converted into a hexadecimal string)
        byte[] cipherDataByte = Hex.decode(cipherData);
        //Get the basic domain parameters of the private key through the private key object.
        ECParameterSpec ecParameterSpec = privateKey.getParameters();
        ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                ecParameterSpec.getG(), ecParameterSpec.getN());
        //Create a private key parameter object through the private key value and basic private key parameters
        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),
                ecDomainParameters);
        //Create and initialize the decryption engine through decryption mode
        SM2Engine sm2Engine = new SM2Engine(mode);
        sm2Engine.init(false, ecPrivateKeyParameters);
        String result = null;
        try {
            //Decrypt the cipher text string through the decryption engine
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
            //Convert the decrypted byte string into a utf8 character encoding string (needs to be consistent with the character encoding specified when the string is converted into a byte string when plaintext encryption is performed)
            result = new String(arrayOfBytes, "utf-8");
        } catch (Exception e) {
            System.out.println("Exception occurred during SM2 decryption" + e.getMessage());
        }
        return result;
    }
    //Elliptic curve ECParameters ASN.1 structure
    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    //The basic domain parameters of the elliptic curve public key or private key.
    private static ECParameterSpec ecDomainParameters = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());

    /**
     * @Description Convert public key string to BCECPublicKey public key object
     * @Author msx
     * @param pubKeyHex 64-byte hexadecimal public key string (if the public key string is 65 bytes and the first byte is 0x04: it means that the public key is in uncompressed format and needs to be deleted during operation)
     * @return BCECPublicKey SM2 public key object
     */
    public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex) {
        //Intercept the 64-byte valid SM2 public key (if the first byte of the public key is 0x04)
        if (pubKeyHex.length() > 128) {
            pubKeyHex = pubKeyHex.substring(pubKeyHex.length() - 128);
        }
        //Split the public key into x and y components (32 bytes each)
        String stringX = pubKeyHex.substring(0, 64);
        String stringY = pubKeyHex.substring(stringX.length());
        //Convert the x and y components of the public key to BigInteger type
        BigInteger x = new BigInteger(stringX, 16);
        BigInteger y = new BigInteger(stringY, 16);
        //Create an elliptic curve public key specification through the x and y components of the public key
        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecDomainParameters);
        //Create an elliptic curve public key object through the elliptic curve public key specification (can be used for SM2 encryption and signature verification)
        return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
    }

    /**
     * @Description Convert private key string to BCECPrivateKey private key object
     * @Author msx
     * @param privateKeyHex 32-byte hexadecimal private key string
     * @return BCECPrivateKey SM2 private key object
     */
    public static BCECPrivateKey getBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) {
        //Convert the hexadecimal private key string to a BigInteger object
        BigInteger d = new BigInteger(privateKeyHex, 16);
        //Create an elliptic curve private key specification through the private key and private key domain parameter sets
        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecDomainParameters);
        //Create an elliptic curve private key object through the elliptic curve private key specification (can be used for SM2 decryption and signature)
        return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
    }

    public static void main(String[] args) {
        String publicKeyHex = null;
        String privateKeyHex = null;
        KeyPair keyPair = createECKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        if (publicKey instanceof BCECPublicKey) {
            //Get the 65-byte uncompressed hexadecimal public key string (0x04)
            publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
            System.out.println("---->SM2 public key:" + publicKeyHex);
        }
        PrivateKey privateKey = keyPair.getPrivate();
        if (privateKey instanceof BCECPrivateKey) {
            //Get the 32-byte hexadecimal private key string
            privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
            System.out.println("---->SM2 private key:" + privateKeyHex);
        }

        /**
         * Public key encryption
         */
        String data = "==========Data to be encrypted=========";

        //Convert hexadecimal public key string to BCECPublicKey public key object
        String encryptData = encrypt1(publicKeyHex, data);
        System.out.println("---->Encryption result:" + encryptData);

        /**
         * Private key decryption
         */
        //Convert the hexadecimal private key string to the BCECPrivateKey private key object
        data = decrypt1(privateKeyHex, encryptData);
        System.out.println("---->Decryption result:" + data);
    }
}

Finally, by calling the decryption method of the tool class in the back-end interface, the encrypted data transmitted from the front-end can be decrypted.

At this point, ヽ(°▽°)ノ? sprinkles flowers.

This chapter is temporarily finished.