当前位置:   article > 正文

【Java】国密SM3/SM4(附工具类)_java sm3

java sm3

前言

  • 目前形式国内都在推行国产化,例如:国产化数据库,国产化服务器等。
  • 我上家公司其中一条产品线是做电网项目的,其中加密的方式就要求使用国密加密算法,SM3和SM4,所以说国产化应该是一个趋势。
  • 国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。

国密算法

  • 国密算法,全称为中国密码算法国家标准,是由中国国家密码管理局主导制定的一套密码算法标准。
  • 国密算法包括以下四种:
    • SM1对称加密算法
    • SM2非对称加密算法
    • SM3杂凑算法
    • SM4分组密码算法
  • 国密算法在国家信息安全领域得到了广泛应用,被用于政务通信、金融支付、电子商务、云计算等领域。
  • 其目的是为了保护国家信息安全和个人隐私,增强国内密码产品和系统的自主可控能力。

SM1对称加密算法

  • SM1对称加密算法是一种高效的分组密码算法,适用于对称加密和消息认证码,其安全性与国际上的AES算法相近。

SM1算法的基本原理

  • SM1算法是一种分组密码,采用对称密钥方式,将明文按照固定长度分组,然后对每个分组进行加密和解密。
  • 该算法的关键在于密钥的生成和密钥的长度。
  • SM1算法的密钥长度为128位,密钥生成过程中采用了多种随机数生成器,保证了密钥的随机性和安全性。

SM1算法的应用场景

  1. 电子政务:SM1算法可以用于电子政务中的公文传输、公文存储和电子签章等场景,保证数据的机密性和完整性。
  2. 金融行业:SM1算法可以用于金融行业中的网上银行、移动支付和电子钱包等场景,保护用户的个人信息和资金安全。
  3. 医疗卫生:SM1算法可以用于医疗卫生中的电子病历、远程医疗和医疗数据存储等场景,确保医疗信息的机密性和完整性。
  4. 物联网:SM1算法可以用于物联网中的传感器网络、智能家居和智能交通等场景,保障物联网设备的通信安全和数据隐私。
  5. 云计算:SM1算法可以用于云计算中的数据加密、云存储和虚拟化等场景,保证云服务的安全性和可靠性。

SM1算法的优势

  1. 高安全性:SM1算法的密钥长度为128位,相比其他对称加密算法具有更高的安全性。此外,该算法的密钥生成过程中采用了多种随机数生成器,保证了密钥的随机性和安全性。
  2. 高效性:SM1算法的加解密速度较快,适用于大规模的数据加解密和安全通信同。时,该算法的软硬件实现成本较低,方便在各种设备上应用。
  3. 广泛适用性:SM1算法适用于各种不同的应用场景,包括电子政务、金融行业、医疗卫生、物联网和云计算等。该算法的应用范围广泛,可以满足不同领域的安全需求。
  4. 国家标准:SM1算法是中国的国家密码标准之一,被广泛应用于国家重要信息系统的数据加密和安全通信领域。该算法得到了国家的支持和认可,具有较高的可靠性和安全性。

代码示例

import javax.crypto.Cipher;  
import javax.crypto.spec.SecretKeySpec;  
import java.nio.charset.StandardCharsets;  
import java.util.Base64;  
  
public class AESUtil {  
  
    private static final String ALGORITHM = "AES";  
    private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding"; // 使用ECB模式和PKCS5Padding填充  
  
    // 使用AES加密  
    public static String encrypt(String key, String value) throws Exception {  
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);  
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);  
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);  
        byte[] encrypted = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));  
        return Base64.getEncoder().encodeToString(encrypted);  
    }  
  
    // 使用AES解密  
    public static String decrypt(String key, String encrypted) throws Exception {  
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);  
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);  
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);  
        byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));  
        return new String(original, StandardCharsets.UTF_8);  
    }  
  
    // 示例用法  
    public static void main(String[] args) throws Exception {  
        String key = "your_16_byte_key_here_for_aes"; // AES密钥需要是16字节(128位)  
        String value = "Hello, World!";  
  
        String encrypted = encrypt(key, value);  
        System.out.println("Encrypted: " + encrypted);  
  
        String decrypted = decrypt(key, encrypted);  
        System.out.println("Decrypted: " + decrypted);  
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

SM2非对称加密算法

  • SM2非对称加密算法是基于椭圆曲线密码体制的数字签名算法和密钥交换协议,安全性较高。

SM2算法的基本原理

  • SM2使用非对称密钥加密体制,包括公钥和私钥。
  • 公钥用于加密数据和验证数字签名,私钥用于解密数据和生成数字签名。

SM2算法的应用场景

  1. 数据加密:
    • SM2算法可用于加密和解密数据,保障数据传输的安全性。它利用公钥进行加密,私钥进行解密,确保数据在传输过程中的机密性。
    • 在网络安全领域,SM2算法常被用于保护敏感信息的传输,如电子邮件加密、文件传输加密等。
  2. 数字签名:
    • SM2算法可用于生成数字签名,验证文档或数据的完整性和真实性。通过私钥对消息进行签名,利用公钥进行验证,确保数据在传输过程中未被篡改,并验证发送方的身份。
    • 数字签名在电子认证、电子合同、电子票据等领域具有广泛应用,如网上银行交易、电子政务等。
  3. 身份认证:
    • SM2算法可用于身份认证,验证用户的身份信息。通过公钥和私钥的配对使用,可以确保用户身份的真实性和可信度。
    • 在网络安全领域,SM2算法常被用于用户登录、权限管理等场景,保障系统的安全性和可信度。
  4. 密钥交换:
    • SM2算法支持密钥交换功能,通过公钥和私钥的配对使用,可以安全地协商出共享密钥,用于后续的对称加密通信。
    • 密钥交换在网络安全通信中具有重要意义,可以确保通信双方之间密钥的安全传输和共享。
  5. 国家安全领域:
    • 作为中国国家密码管理局颁布的国家标准,SM2算法在中国国内得到广泛应用,特别是在政府机构和企业的信息安全领域。
    • 它符合国家密码管理政策,有利于保障国家信息安全,防止外部攻击和数据泄露。
  6. 国际认可:
    • 虽然SM2是中国的国家标准,但它在一定程度上也获得了国际认可。部分国际组织和标准化机构也开始考虑将其作为一种可选的加密算法。

SM2算法的优势

  1. 安全性高:
    • SM2算法基于椭圆曲线离散对数难题,这一数学问题的复杂性保证了算法的安全性。
    • 与RSA算法相比,SM2算法在同等安全水平下,所需的密钥长度和签名长度更短,从而降低了被破解的风险。
    • SM2算法被认为在经典计算机和量子计算机的攻击下都是安全的。
  2. 效率高:
    • SM2算法具有较高的运算效率,特别是在加密、解密和数字签名方面,性能优于RSA算法。
    • 它适合高并发场景,满足大量数据加密、解密和数字签名的需求。
    • SM2算法的CPU占用少、内容使用少、网络消耗低,特别适用于移动设备和资源受限的环境。
  3. 灵活性好:
    • SM2算法支持多种密钥长度,可以根据实际需求灵活选择密钥长度,适用于不同的应用场景。
  4. 自主创新:
    • 它符合国家密码管理政策,有利于保障国家关键信息系统的信息安全。
  5. 技术特点:
    • SM2算法基于椭圆曲线密码学,具备ECC算法的性能特点并实现优化改进。
    • 它采用了一系列安全性增强措施,如密钥派生函数和随机数生成算法,以防止各种攻击。

代码示例

依赖
<dependency>  
    <groupId>org.bouncycastle</groupId>  
    <artifactId>bcprov-jdk15on</artifactId>  
    <version>最新版本</version>  
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
代码
import org.bouncycastle.crypto.CipherParameters;  
import org.bouncycastle.crypto.engines.SM2Engine;  
import org.bouncycastle.crypto.params.ECPublicKeyParameters;  
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;  
import org.bouncycastle.crypto.params.SM2PrivateKeyParameters;  
import org.bouncycastle.crypto.params.SM2PublicKeyParameters;  
import org.bouncycastle.jce.ECNamedCurveGenParameterSpec;  
import org.bouncycastle.jce.ECNamedCurveTable;  
import org.bouncycastle.jce.provider.BouncyCastleProvider;  
import org.bouncycastle.jce.spec.ECParameterSpec;  
  
import java.security.KeyPair;  
import java.security.KeyPairGenerator;  
import java.security.PrivateKey;  
import java.security.PublicKey;  
import java.security.Security;  
  
public class SM2Utils {  
  
    static {  
        Security.addProvider(new BouncyCastleProvider());  
    }  
  
    // 生成SM2密钥对  
    public static KeyPair generateKeyPair() throws Exception {  
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");  
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("sm2p256v1");  
        keyGen.initialize(new ECNamedCurveGenParameterSpec("sm2p256v1"), new java.security.SecureRandom());  
        return keyGen.generateKeyPair();  
    }  
  
    // 此处仅为示例,实际使用中需要实现更完整的加密/解密/签名/验签逻辑  
    // 加密/解密、签名/验签的逻辑将涉及更复杂的参数设置和异常处理  
  
    // 注意:Bouncy Castle的org.bouncycastle.crypto包与Java的javax.crypto包不同  
    // 下面的示例仅为框架,并未包含完整的加密/解密逻辑  
    public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {  
        // 需要实现使用SM2公钥的加密逻辑  
        // ...  
        return null; // 示例返回null,实际应返回加密后的数据  
    }  
  
    public static byte[] decrypt(PrivateKey privateKey, byte[] cipherText) throws Exception {  
        // 需要实现使用SM2私钥的解密逻辑  
        // ...  
        return null; // 示例返回null,实际应返回解密后的数据  
    }  
  
    public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception {  
        // 需要实现使用SM2私钥的签名逻辑  
        // ...  
        return null; // 示例返回null,实际应返回签名数据  
    }  
  
    public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws Exception {  
        // 需要实现使用SM2公钥的验签逻辑  
        // ...  
        return false; // 示例返回false,实际应返回验签结果  
    }  
  
    // ... 其他辅助方法或工具方法 ...  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

SM3杂凑算法

  • SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。
  • SM3杂凑算法是一种散列算法,用于将输入的数据转化为固定长度的摘要,具有抗碰撞、抗第二原像和抗预像等特性。
  • SM3杂凑算法是我国自主设计的密码杂凑算法,适用于商用密码应用中的数字签名和验证消息认证码的生成与验证以及随机数的生成,可满足多种密码应用的安全需求。
  • 为了保证杂凑算法的安全性,其产生的杂凑值的长度不应太短,例如MD5输出128比特杂凑值,输出长度太短,影响其安全性SHA-1算法的输出长度为160比特,SM3算法的输出长度为256比特,因此SM3算法的安全性要高于MD5算法和SHA-1算法。

SM4分组密码算法

  • SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。
  • SM4分组密码算法是我国自主设计的分组对称密码算法,用于实现数据的加密/解密运算,以保证数据和信息的机密性。
  • 要保证一个对称密码算法的安全性的基本条件是其具备足够的密钥长度,SM4算法与AES算法具有相同的密钥长度分组长度128比特,因此在安全性上高于3DES算法。

SM3、SM4工具类示例

依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15to18</artifactId>
    <version>1.77</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

SM3工具类

package com.mkj.publicservice.utils.smalgorithm;

import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import java.io.UnsupportedEncodingException;

/**
 * @author: mkj
 */
public class Sm3Util {
    /**
     * SM3加密
     * @param content  要加密的内容
     */
    public static String  Sm3EncryptText(String content) throws UnsupportedEncodingException {
        byte[] bytes = content.getBytes();
        byte[] hash = hash(bytes);
        String sm3 = ByteUtils.toHexString(hash);
        System.out.println("[Sm3EncryptText]sm3 hex:" + sm3);
        return sm3;
    }

    public static byte[] hash(byte[] srcData){
        SM3Digest digest=new SM3Digest();
        digest.update(srcData,0,srcData.length);
        byte[] bytes = new byte[digest.getDigestSize()];
        digest.doFinal(bytes,0);
        return bytes;
    }
    /**
     * sm3算法通过密钥进行加密,不可逆加密
     * @param keyText 密钥字符串
     * @param plainText 需加密的明文字符串
     * @return 加密后固定长度64的16进制字符串
     */
    public static String encryptByKey(String keyText, String plainText) {
        return ByteUtils.toHexString(encryptByKey(keyText.getBytes(), plainText.getBytes()));
    }

    /**
     * sm3算法通过密钥进行加密,不可逆加密
     * @param keyByte 密钥数组
     * @param plainByte 需加密的明文数组
     * @return 加密后固定长度64的16进制数组
     */
    public static byte[] encryptByKey(byte[] keyByte, byte[] plainByte) {
        KeyParameter keyParameter = new KeyParameter(keyByte);
        SM3Digest sm3Digest = new SM3Digest();
        HMac hMac = new HMac(sm3Digest);
        hMac.init(keyParameter);
        hMac.update(plainByte, 0, plainByte.length);
        byte[] result = new byte[hMac.getMacSize()];
        hMac.doFinal(result, 0);
        return result;
    }
    /**
     * 主函数
     * @param args
     */
    public static void main(String[] args) throws UnsupportedEncodingException {
        Sm3EncryptText("ceshi001");
        System.out.println(encryptByKey("key001","abc"));
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

SM4工具类

  • SM4两种算法,基于ECB和CBC。示例中key都使用了16进制的字符串,有的项目没有需求可以自行去除。看实际情况。

CBC

package com.mkj.publicservice.utils.smalgorithm;

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.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

/**
 * @author: mkj
 */
public class Sm4CbcUtils {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ENCODING = "UTF-8";

    public static final String ALGORITHM_NAME = "SM4";
    // 加密算法/分组加密模式/分组填充方式
    // PKCS5Padding-以8个字节为一组进行分组加密
    // 定义分组加密模式使用:PKCS5Padding
    public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
    // 128-32位16进制;256-64位16进制
    public static final int DEFAULT_KEY_SIZE = 128;

    /**
     * 自动生成密钥
     *
     * @return
     * @explain
     */
    public static byte[] generateKey() throws Exception {
        return generateKey(DEFAULT_KEY_SIZE);
    }


    /**
     * 自动生成密钥
     * @return
     * @throws Exception
     */
    public static String generateKeyString() throws Exception {
        return ByteUtils.toHexString(generateKey());
    }

    /**
     * @param keySize
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] generateKey(int keySize) throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    /**
     * sm4加密
     *
     * @param hexKey   16进制密钥(忽略大小写)
     * @param paramStr 待加密字符串
     * @return 返回16进制的加密字符串
     * @throws Exception
     * @explain 加密模式:CBC
     */
    public static String encrypt(String hexKey, String paramStr) throws Exception {
        String result = "";
        // 16进制字符串-->byte[]
        byte[] keyData = ByteUtils.fromHexString(hexKey);
        // String-->byte[]
        byte[] srcData = paramStr.getBytes(ENCODING);
        // 加密后的数组
        byte[] cipherArray = encryptCbcPadding(keyData, srcData);

        // byte[]-->hexString
        result = ByteUtils.toHexString(cipherArray);
        return result;
    }

    /**
     * 加密模式之CBC
     *
     * @param key
     * @param data
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] encryptCbcPadding(byte[] key, byte[] data) throws Exception {
        Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * 加密模式之CBC
     * @param algorithmName
     * @param mode
     * @param key
     * @return
     * @throws Exception
     */
    private static Cipher generateCbcCipher(String algorithmName, int mode, byte[] key) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        cipher.init(mode, sm4Key, generateIV());
        return cipher;
    }

    /**
     * 生成iv
     * @return
     * @throws Exception
     */
    public static AlgorithmParameters generateIV() throws Exception {
        //iv 为一个 16 字节的数组,这里采用和 iOS 端一样的构造方法,数据全为0
        byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        AlgorithmParameters params = AlgorithmParameters.getInstance(ALGORITHM_NAME);
        params.init(new IvParameterSpec(iv));
        return params;
    }

    /**
     * sm4解密
     *
     * @param hexKey 16进制密钥
     * @param text   16进制的加密字符串(忽略大小写)
     * @return 解密后的字符串
     * @throws Exception
     * @explain 解密模式:采用CBC
     */
    public static String decrypt(String hexKey, String text) throws Exception {
        // 用于接收解密后的字符串
        String result = "";
        // hexString-->byte[]
        byte[] keyData = ByteUtils.fromHexString(hexKey);
        // hexString-->byte[]
        byte[] resultData = ByteUtils.fromHexString(text);
        // 解密
        byte[] srcData = decryptCbcPadding(keyData, resultData);
        // byte[]-->String
        result = new String(srcData, ENCODING);
        return result;
    }

    /**
     * 解密
     *
     * @param key
     * @param cipherText
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] decryptCbcPadding(byte[] key, byte[] cipherText) throws Exception {
        Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }

    public static void main(String[] args) throws Exception {

        String str = "Hello, world" ;
        System.out.println("==========生成密钥==========");
        String generateKey = generateKeyString();
        System.out.println(generateKey);
        System.out.println("==========加密==========");
        String encrypt = encrypt(generateKey, str);
        System.out.println(encrypt);
        System.out.println("==========解密==========");
        String decrypt = decrypt(generateKey, encrypt);
        System.out.println(decrypt);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181

ECB

package com.mkj.publicservice.utils.smalgorithm;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

/**
 * @author: mkj
 */
public class Sm4EcbUtils {

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

    private static final String ENCODING = "UTF-8";
    public static final String ALGORITHM_NAME = "SM4";
    // 加密算法/分组加密模式/分组填充方式
    // PKCS5Padding-以8个字节为一组进行分组加密
    // 定义分组加密模式使用:PKCS5Padding
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
    // 128-32位16进制;256-64位16进制
    public static final int DEFAULT_KEY_SIZE = 128;

    /**
     * 自动生成密钥
     *
     * @return
     * @explain
     */
    public static String generateKey() throws Exception {
        return new String(Hex.encode(generateKey(DEFAULT_KEY_SIZE)));
    }

    /**
     * @param keySize
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] generateKey(int keySize) throws Exception {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    /**
     * 生成ECB暗号
     *
     * @param algorithmName 算法名称
     * @param mode          模式
     * @param key
     * @return
     * @throws Exception
     * @explain ECB模式(电子密码本模式:Electronic codebook)
     */
    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, ALGORITHM_NAME);
        cipher.init(mode, sm4Key);
        return cipher;
    }

    /**
     * sm4加密
     * @param hexKey   16进制密钥(忽略大小写),也可自定义,注意如果不需要转化16进制,自行修改代码。
     *                 byte[] keyData = ByteUtils.fromHexString(hexKey);
     *                 改为
     *                 byte[] srcData = hexKey.getBytes(ENCODING);
     *                 验证方法为同样的道理
     * @param paramStr 待加密字符串
     * @return 返回16进制的加密字符串
     * @explain 加密模式:ECB
     * 密文长度不固定,会随着被加密字符串长度的变化而变化
     */
    public static String encryptEcb(String hexKey, String paramStr) {
        try {
            String cipherText = "";
            // 16进制字符串-->byte[]
            byte[] keyData = ByteUtils.fromHexString(hexKey);
            // String-->byte[]
            byte[] srcData = paramStr.getBytes(ENCODING);
            // 加密后的数组
            byte[] cipherArray = encryptEcbPadding(keyData, srcData);
            // byte[]-->hexString
            cipherText = ByteUtils.toHexString(cipherArray);
            return cipherText;
        } catch (Exception e) {
            return paramStr;
        }
    }

    /**
     * 加密模式之Ecb
     *
     * @param key
     * @param data
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] encryptEcbPadding(byte[] key, byte[] data) throws Exception {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * sm4解密
     *
     * @param hexKey     16进制密钥
     * @param cipherText 16进制的加密字符串(忽略大小写)
     * @return 解密后的字符串
     * @throws Exception
     * @explain 解密模式:采用ECB
     */
    public static String decryptEcb(String hexKey, String cipherText) {
        // 用于接收解密后的字符串
        String decryptStr = "";
        // hexString-->byte[]
        byte[] keyData = ByteUtils.fromHexString(hexKey);
        // hexString-->byte[]
        byte[] cipherData = ByteUtils.fromHexString(cipherText);
        // 解密
        byte[] srcData = new byte[0];
        try {
            srcData = decryptEcbPadding(keyData, cipherData);
            // byte[]-->String
            decryptStr = new String(srcData, ENCODING);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return decryptStr;
    }

    /**
     * 解密
     *
     * @param key
     * @param cipherText
     * @return
     * @throws Exception
     * @explain
     */
    public static byte[] decryptEcbPadding(byte[] key, byte[] cipherText) throws Exception {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }

    /**
     * 校验加密前后的字符串是否为同一数据
     *
     * @param hexKey     16进制密钥(忽略大小写)
     * @param cipherText 16进制加密后的字符串
     * @param paramStr   加密前的字符串
     * @return 是否为同一数据
     * @throws Exception
     * @explain
     */
    public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception {
        // 用于接收校验结果
        boolean flag = false;
        // hexString-->byte[]
        byte[] keyData = ByteUtils.fromHexString(hexKey);
        // 将16进制字符串转换成数组
        byte[] cipherData = ByteUtils.fromHexString(cipherText);
        // 解密
        byte[] decryptData = decryptEcbPadding(keyData, cipherData);
        // 将原字符串转换成byte[]
        byte[] srcData = paramStr.getBytes(ENCODING);
        // 判断2个数组是否一致
        flag = Arrays.equals(decryptData, srcData);
        return flag;
    }

    public static void main(String[] args) {
        try {
            String paramStr = "Hello, world";
            System.out.println("==========加密前源数据==========");
            System.out.println(paramStr);
            // 生成32位16进制密钥
            String key = generateKey();
            System.out.println("==========生成key==========");
            System.out.println(key);
            String cipher = encryptEcb(key, paramStr);
            System.out.println("==========加密串==========");
            System.out.println(cipher);
            System.out.println("==========是否为同一数据==========");
            System.out.println(verifyEcb(key, cipher, paramStr));
            paramStr = decryptEcb(key, cipher);
            System.out.println("==========解密后数据==========");
            System.out.println(paramStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号