JAVA secret national intégré SM4

Un aperçu de l'algorithme secret national : https://blog.csdn.net/qq_38254635/article/details/131801527

Algorithme symétrique SM4
Algorithme de données par paquets standard SM4 WLAN. Cryptage symétrique, la longueur de clé et la longueur de bloc sont de 128 bits

1. configuration du pompon

<!-- 国密 -->
<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcprov-jdk15to18</artifactId>
	<version>1.66</version>
</dependency>

2. Intégration de codes

2.1. Structure du répertoire

insérez la description de l'image ici

2.2. Code source

ConfigBean.java

package com.secret.sm4;

public class ConfigBean {
    
    

    /**
     * 秘钥空间大小
     */
    public static final int SM4_KEY_SIZE = 128;

    /**
     * 算法编号
     */
    public static final String ALGORITHM_NAME  = "SM4";

    /**
     * 首次加密初始向量  01030507090B0D0F11131517191B1D1F
     */
    public static final byte[] SM4_KEY_IV = {
    
     1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 };

    /**
     * ECB模式串
     */
    public static final String ALGORITHM_ECB_PADDING = "SM4/ECB/PKCS5Padding";
    /**
     * CBC模式串
     */
    public static final String ALGORITHM_CBC_PADDING = "SM4/CBC/PKCS5Padding";

}

FournisseurSingleton.java

package com.secret.sm4;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class ProviderSingleton {
    
    

    private static BouncyCastleProvider instance = null;

    private ProviderSingleton() {
    
    
    }

    private static synchronized void syncInit() {
    
    
        if (instance == null) {
    
    
            instance = new BouncyCastleProvider();
        }
    }

    public static BouncyCastleProvider getInstance() {
    
    
        if (instance == null) {
    
    
            syncInit();
        }
        return instance;
    }

}

SecretCommon.java

package com.secret.sm4;

import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;

/**
 * SM4分组对称密码算法(对称算法)
 * SM4分组密码算法是我国自主设计的分组对称密码算法,用于实现数据的加密/解密运算,以保证数据和信息的机密性。
 * 要保证一个对称密码算法的安全性的基本条件是其具备足够的密钥长度,SM4算法与AES算法具有相同的密钥长度分组长度128比特,因此在安全性上高于3DES算法。
 */
public class SecretCommon {
    
    

    static {
    
    
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 生成秘钥
     */
    public static String generateKey() throws NoSuchAlgorithmException {
    
    
        return ByteUtils.toHexString(generateKeyByte(ConfigBean.SM4_KEY_SIZE, ProviderSingleton.getInstance()));
    }

    /**
     * 生成秘钥
     */
    public static String generateKeyBC() throws NoSuchProviderException, NoSuchAlgorithmException {
    
    
        return ByteUtils.toHexString(generateKeyByte(ConfigBean.SM4_KEY_SIZE, BouncyCastleProvider.PROVIDER_NAME));
    }

    public static byte[] generateKeyByte(int keySize, String var) throws NoSuchAlgorithmException, NoSuchProviderException {
    
    
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ConfigBean.ALGORITHM_NAME, var);
        keyGenerator.init(keySize, new SecureRandom());
        return keyGenerator.generateKey().getEncoded();
    }

    public static byte[] generateKeyByte(int keySize, BouncyCastleProvider var) throws NoSuchAlgorithmException {
    
    
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ConfigBean.ALGORITHM_NAME, var);
        keyGenerator.init(keySize, new SecureRandom());
        return keyGenerator.generateKey().getEncoded();
    }

    /**
     * ECB模式,加密
     * @param plainText 明文字符串
     * @param keyText 秘钥内容
     */
    public static String encryptECB(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return Base64.encodeBase64String(cipherECB(Cipher.ENCRYPT_MODE, plainText.getBytes(StandardCharsets.UTF_8), keyText.getBytes()));
    }

    /**
     * ECB模式,解密
     * @param cipherText 密文字符串
     * @param keyText 秘钥内容
     */
    public static String decryptECB(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return new String(cipherECB(Cipher.DECRYPT_MODE, Base64.decodeBase64(cipherText), keyText.getBytes()));
    }

    /**
     * ECB模式,加解密算法基础方法
     * @param mode 加解密模式 Cipher.ENCRYPT_MODE 1:加密/ Cipher.DECRYPT_MODE 2:解密
     * @param plainByte 需加密明文内容/待解密密文内容
     * @param keyByte 秘钥内容
     */
    public static byte[] cipherECB(int mode, byte[] plainByte, byte[] keyByte) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        Cipher cipher = Cipher.getInstance(ConfigBean.ALGORITHM_ECB_PADDING, ProviderSingleton.getInstance());
        cipher.init(mode, new SecretKeySpec(keyByte, ConfigBean.ALGORITHM_NAME));
        return cipher.doFinal(plainByte);
    }

    /**
     * CBC模式,加密
     * @param plainText 明文字符串
     * @param keyText 秘钥内容
     */
    public static String encryptCBC(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return Base64.encodeBase64String(cipherCBC(Cipher.ENCRYPT_MODE, plainText.getBytes(StandardCharsets.UTF_8), keyText.getBytes()));
    }

    /**
     * CBC模式,解密
     * @param cipherText 密文字符串
     * @param keyText 秘钥内容
     */
    public static String decryptCBC(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return new String(cipherCBC(Cipher.DECRYPT_MODE, Base64.decodeBase64(cipherText), keyText.getBytes()));
    }

    /**
     * CBC模式,加解密算法基础方法
     * @param mode 加解密模式 Cipher.ENCRYPT_MODE 1:加密/ Cipher.DECRYPT_MODE 2:解密
     * @param plainByte 需加密明文内容/待解密密文内容
     * @param keyByte 秘钥内容
     */
    public static byte[] cipherCBC(int mode, byte[] plainByte, byte[] keyByte) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        Cipher cipher = Cipher.getInstance(ConfigBean.ALGORITHM_CBC_PADDING, ProviderSingleton.getInstance());
        cipher.init(mode, new SecretKeySpec(keyByte, ConfigBean.ALGORITHM_NAME), new IvParameterSpec(ConfigBean.SM4_KEY_IV));
        return cipher.doFinal(plainByte);
    }

}

Utils.java

package com.secret.sm4;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class Utils {
    
    

    /**
     * 生成秘钥
     */
    public static String generateKey() throws NoSuchAlgorithmException {
    
    
        return SecretCommon.generateKey().substring(0, 16);
    }

    /**
     * 默认加密方法(ECB)
     */
    public static String encrypt(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return encryptECB(plainText, keyText);
    }

    /**
     * 默认解密方法(ECB)
     */
    public static String decrypt(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return decryptECB(cipherText, keyText);
    }

    /**
     * ECB模式,加密
     * @param plainText 明文字符串
     * @param keyText 秘钥内容
     */
    public static String encryptECB(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return SecretCommon.encryptECB(plainText, keyText);
    }

    /**
     * ECB模式,解密
     * @param cipherText 密文字符串
     * @param keyText 秘钥内容
     */
    public static String decryptECB(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return SecretCommon.decryptECB(cipherText, keyText);
    }

    /**
     * CBC模式,加密
     * @param plainText 明文字符串
     * @param keyText 秘钥内容
     */
    public static String encryptCBC(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return SecretCommon.encryptCBC(plainText, keyText);
    }

    /**
     * CBC模式,解密
     * @param cipherText 密文字符串
     * @param keyText 秘钥内容
     */
    public static String decryptCBC(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    
    
        return SecretCommon.decryptCBC(cipherText, keyText);
    }

}

Classe de test : Test.java

package com.secret.sm4;

public class Test {
    
    

    public static void main(String[] args) throws Exception{
    
    
        String key = Utils.generateKey();
        System.out.println("生成SM4秘钥:" + key);

        String plainText = "Believe in yourself, you are the best";
        String ECBText = Utils.encrypt(plainText, key);
        System.out.println("ECB默认加密后密文:" + ECBText);
        System.out.println("ECB默认解密后明文:" + Utils.decrypt(ECBText, key));

        String CBCText = Utils.encryptCBC(plainText, key);
        System.out.println("CBC加密后密文:" + CBCText);
        System.out.println("CBC解密后明文:" + Utils.decryptCBC(CBCText, key));
    }

}

2.3. Essai

insérez la description de l'image ici
Reportez-vous à la classe de test pour la méthode d'utilisation.

3. La fosse rencontrée

3.1. Longueur de la clé

La longueur de la clé secrète construite par KeyGenerator est trop longue pour être utilisée, et il vous demandera :
insérez la description de l'image ici
la clé secrète peut être utilisée avec 16 chiffres.

3.2, problème de transcodage

Lors de l'utilisation du mode ECB, le cryptage et le décryptage signaleront une erreur, et la tête est grosse.

L'erreur de chiffrement est la suivante :

Exception in thread "main" java.lang.IllegalArgumentException: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible value. Expected the discarded bits to be zero.
	at org.apache.tomcat.util.codec.binary.Base64.validateCharacter(Base64.java:429)
	at org.apache.tomcat.util.codec.binary.Base64.decode(Base64.java:671)
	at org.apache.tomcat.util.codec.binary.BaseNCodec.decode(BaseNCodec.java:362)
	at org.apache.tomcat.util.codec.binary.BaseNCodec.decode(BaseNCodec.java:353)
	at org.apache.tomcat.util.codec.binary.BaseNCodec.decode(BaseNCodec.java:379)
	at org.apache.tomcat.util.codec.binary.Base64.decodeBase64(Base64.java:172)
	at com.secret.sm4.SecretCommon.encryptECB(SecretCommon.java:64)
	at com.secret.sm4.Utils.encryptECB(Utils.java:39)
	at com.secret.sm4.Utils.encrypt(Utils.java:23)
	at com.secret.sm4.Test.main(Test.java:10)

insérez la description de l'image ici

L'erreur de déchiffrement est la suivante :

Exception in thread "main" javax.crypto.BadPaddingException: pad block corrupted
	at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(Unknown Source)
	at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
	at javax.crypto.Cipher.doFinal(Cipher.java:2165)
	at com.secret.sm4.SecretCommon.cipherECB(SecretCommon.java:85)
	at com.secret.sm4.SecretCommon.decryptECB(SecretCommon.java:73)
	at com.secret.sm4.Utils.decryptECB(Utils.java:48)
	at com.secret.sm4.Utils.decrypt(Utils.java:30)
	at com.secret.sm4.Test.main(Test.java:12)

insérez la description de l'image ici

Après avoir consulté les informations, j'ai découvert qu'il s'agissait d'un problème de conversion des tableaux byte[].

String plainText = "Believe in yourself, you are the best";
byte[] byte1 = plainText.getBytes();
byte[] byte2 = plainText.getBytes(StandardCharsets.UTF_8);
byte[] byte3 = Base64.decodeBase64(plainText);
byte[] byte4 = Hex.decode(plainText);
Base64.encodeBase64String()
new String()

Lors du test, j'ai constaté que la même conversion en byte[], différentes méthodes, le résultat peut être différent, vous pouvez essayer de le vérifier vous-même.

Lors du chiffrement :
utilisez getBytes() directement pour obtenir le tableau d'octets pour les paramètres d'entrée et utilisez Base64.encodeBase64String() pour les paramètres de retour.
Lors du décryptage :
utilisez Base64.decodeBase64() pour obtenir le tableau d'octets comme paramètre d'entrée, et utilisez simplement new String() comme paramètre de retour.

4. Liens connexes

Un aperçu de l'algorithme secret national : https://blog.csdn.net/qq_38254635/article/details/131801527

JAVA secret national intégré SM2 : https://blog.csdn.net/qq_38254635/article/details/131810661

JAVA secret national intégré SM3 : https://blog.csdn.net/qq_38254635/article/details/131810696

L'impact du mode singleton et de la simultanéité multithread en mode singleton : https://blog.csdn.net/qq_38254635/article/details/119888843

Je suppose que tu aimes

Origine blog.csdn.net/qq_38254635/article/details/131810715
conseillé
Classement