sm4和sm2混合加密

138 阅读3分钟

描述

使用sm2加密key,使用sm4加密内容,实现混合加密,sm2为类似RSA的非对称加密,sm4为类似AES的对称加密,混合加密的好处为提高效率和安全的均衡性

代码

package org.example.test;

import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SM2MixedSM4 {

    static {
        Security.addProvider(new BouncyCastleProvider()); // 注册BouncyCastle
    }

    public static SecretKey generateSM4Key() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("SM4", "BC"); // 指定算法和提供商
        keyGenerator.init(128); // SM4密钥长度固定128位
        return keyGenerator.generateKey();
    }

    public static String secretKeyStr() throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("SM4", "BC"); // 指定算法和提供商
        keyGenerator.init(128); // SM4密钥长度固定128位
        byte[] encoded = keyGenerator.generateKey().getEncoded();
        return Base64.getEncoder().encodeToString(encoded);
    }

    public static byte[] sm4Encrypt(byte[] plaintext, SecretKey key) throws Exception {
        // 生成随机IV(16字节)
        byte[] iv = new byte[16];
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);

        // 初始化加密器
        Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

        // 加密数据(最终密文 = IV + 加密内容)
        byte[] ciphertext = cipher.doFinal(plaintext);
        return concatenateArrays(iv, ciphertext); // 将IV与密文拼接
    }

    // 工具方法:拼接两个字节数组
    private static byte[] concatenateArrays(byte[] a, byte[] b) {
        byte[] result = new byte[a.length + b.length];
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    }


    public static byte[] sm4Decrypt(byte[] ciphertextWithIv, SecretKey key) throws Exception {
        // 分离IV和密文(前16字节是IV)
        byte[] iv = new byte[16];
        byte[] ciphertext = new byte[ciphertextWithIv.length - 16];
        System.arraycopy(ciphertextWithIv, 0, iv, 0, 16);
        System.arraycopy(ciphertextWithIv, 16, ciphertext, 0, ciphertext.length);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);

        // 初始化解密器
        Cipher cipher = Cipher.getInstance("SM4/CBC/PKCS7Padding", "BC");
        cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

        // 解密数据
        return cipher.doFinal(ciphertext);
    }

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

        //        // 初始化密钥生成器
//        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "BC");
//        ECGenParameterSpec ecSpec = new ECGenParameterSpec("sm2p256v1");
//        generator.initialize(ecSpec, new SecureRandom());
//        KeyPair keyPair = generator.generateKeyPair();
//
//        // 提取公钥和私钥
//        PublicKey publicKey = keyPair.getPublic();
//        PrivateKey privateKey = keyPair.getPrivate();
//
//        System.out.println(Base64.toBase64String(publicKey.getEncoded()));
//
//        System.out.println(Base64.toBase64String(privateKey.getEncoded()));

        String publicStr = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAExDwKGxieYLPOHa4M8lwKKjY5YW+oK07w/YhE6fZmJtFeyRgz/DywukVWirCfmFT0axnU3741a6vt2uqLIAe9ZA==";

        String privateStr = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQggFR8H5ZGWwuCmndzuv2OtHtMuK0UnZOiMqFVgorP1yGgCgYIKoEcz1UBgi2hRANCAATEPAobGJ5gs84drgzyXAoqNjlhb6grTvD9iETp9mYm0V7JGDP8PLC6RVaKsJ+YVPRrGdTfvjVrq+3a6osgB71k";


        SecretKey secretKey = generateSM4Key();
        String keyContent  = Base64.getEncoder().encodeToString(secretKey.getEncoded());

        byte[] decodePublicStr = org.bouncycastle.util.encoders.Base64.decode(publicStr);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodePublicStr);

        KeyFactory instance = KeyFactory.getInstance("EC", "BC");

        PublicKey publicKey = instance.generatePublic(x509EncodedKeySpec);
        // 转换公钥为BC格式参数
        ECPublicKeyParameters pubKeyParams = (ECPublicKeyParameters)
                ECUtil.generatePublicKeyParameter(publicKey);

        // 初始化加密引擎(C1C3C2模式)
        SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        engine.init(true, new ParametersWithRandom(pubKeyParams, new SecureRandom()));

        // 加密key
        byte[] plaintext = keyContent.getBytes(StandardCharsets.UTF_8);
        byte[] ciphertext = engine.processBlock(plaintext, 0, plaintext.length);


        // 加密数据
        String content = "你好,sim !";

        byte[] contentBytes = sm4Encrypt(content.getBytes("UTF-8"), secretKey);

        // 拼接key+ 数据
        byte[] bytes = concatenateArrays(ciphertext, contentBytes);

        String  dataToTransfer = Base64.getEncoder().encodeToString(bytes);

        System.out.println(dataToTransfer);



        // 解密

        byte[] decode = Base64.getDecoder().decode(dataToTransfer);

        int keySize = 121;

        byte[] encryptedKey = new byte[keySize];
        byte[] encryptedDataBytes = new byte[decode.length - keySize];

        System.arraycopy(decode, 0, encryptedKey, 0, keySize);
        System.arraycopy(decode, keySize, encryptedDataBytes, 0, encryptedDataBytes.length);


        byte[] decodePrivateStr = org.bouncycastle.util.encoders.Base64.decode(privateStr);
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decodePrivateStr);
        KeyFactory instance1 = KeyFactory.getInstance("EC", "BC");
        PrivateKey privateKey = instance1.generatePrivate(pkcs8EncodedKeySpec);

        // 转换私钥为BC格式参数
        ECPrivateKeyParameters priKeyParams = (ECPrivateKeyParameters)
                ECUtil.generatePrivateKeyParameter(privateKey);

        // 初始化解密引擎(需与加密模式一致)
        SM2Engine engine2 = new SM2Engine(SM2Engine.Mode.C1C3C2);
        // 初始化解密引擎(需与加密模式一致)
        engine2.init(false, priKeyParams);

        // 解密key
        byte[] decrypted = engine2.processBlock(encryptedKey, 0, encryptedKey.length);

        byte[] decode1 = Base64.getDecoder().decode(new String(decrypted, StandardCharsets.UTF_8));


        SecretKeySpec key = new SecretKeySpec(decode1,"SM4");


        // 解密数据
        byte[] decryptedData = sm4Decrypt(encryptedDataBytes, key);
        String result = new String(decryptedData, "UTF-8");

        System.out.println(result);


    }



}