java-信息安全(二十)國密算法 SM1,SM2,SM3,SM4


一、概述

國密即國家密碼局認定的國產密碼算法。主要有SM1,SM2,SM3,SM4。密鑰長度和分組長度均為128位。目前主要使用公開的SM2、SM3、SM4三類算法,分別是非對稱算法、哈希算法和對稱算法。

SM1 為對稱加密。其加密強度與AES相當。該算法不公開,調用該算法時,需要通過加密芯片的接口進行調用。不討論

SM2 為非對稱加密,基於ECC。該算法已公開。由於該算法基於ECC,故其簽名速度與秘鑰生成速度都快於RSA。ECC 256位(SM2采用的就是ECC 256位的一種)安全強度比RSA 2048位高,但運算速度快於RSA。

  SM2橢圓曲線公鑰密碼算法是我國自主設計的公鑰密碼算法,包括SM2-1橢圓曲線數字簽名算法,SM2-2橢圓曲線密鑰交換協議,SM2-3橢圓曲線公鑰加密算法,分別用於實現數字簽名密鑰協商和數據加密等功能。SM2算法與RSA算法不同的是,SM2算法是基於橢圓曲線上點群離散對數難題,相對於RSA算法,256位的SM2密碼強度已經比2048位的RSA密碼強度要高。

SM3 消息摘要。可以用MD5作為對比理解。該算法已公開。校驗結果為256位。

  SM3雜湊算法是我國自主設計的密碼雜湊算法,適用於商用密碼應用中的數字簽名和驗證消息認證碼的生成與驗證以及隨機數的生成,可滿足多種密碼應用的安全需求。為了保證雜湊算法的安全性,其產生的雜湊值的長度不應太短,例如MD5輸出128比特雜湊值,輸出長度太短,影響其安全性SHA-1算法的輸出長度為160比特,SM3算法的輸出長度為256比特,因此SM3算法的安全性要高於MD5算法和SHA-1算法。

SM4 無線局域網標准的分組數據算法。對稱加密,密鑰長度和分組長度均為128位。

  SM4分組密碼算法是我國自主設計的分組對稱密碼算法,用於實現數據的加密/解密運算,以保證數據和信息的機密性。要保證一個對稱密碼算法的安全性的基本條件是其具備足夠的密鑰長度,SM4算法與AES算法具有相同的密鑰長度分組長度128比特,因此在安全性上高於3DES算法。

由於SM1、SM4加解密的分組大小為128bit,故對消息進行加解密時,若消息長度過長,需要進行分組,要消息長度不足,則要進行填充。

代碼地址:https://github.com/bjlhx15/algorithm-sign.git

工具類

package com.github.bjlhx15.security.sm;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @author lihongxu6
 * @version 1.0
 * @className KeyUtils
 * @description TODO
 * @date 2021-01-13 23:27
 */
public class KeyUtils {
    /**
     * 生成國密公私鑰對
     * <p>
     * <code>String[0]</code> 公鑰
     * <p>
     * <code>String[1]</code> 私鑰
     *
     * @return
     * @throws Exception
     */
    public static String[] generateSmKey() throws Exception {
        KeyPairGenerator keyPairGenerator = null;
        SecureRandom secureRandom = new SecureRandom();
        ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
        keyPairGenerator = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
        keyPairGenerator.initialize(sm2Spec);
        keyPairGenerator.initialize(sm2Spec, secureRandom);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        String[] result = {
                new String(Base64.getEncoder().encode(publicKey.getEncoded()))
                , new String(Base64.getEncoder().encode(privateKey.getEncoded()))
        };
        return result;
    }
    /**
     * 將Base64轉碼的公鑰串,轉化為公鑰對象
     *
     * @param publicKey
     * @return
     */
    public static PublicKey createPublicKey(String publicKey) {
        PublicKey publickey = null;
        try{
            X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
            KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());
            publickey = keyFactory.generatePublic(publicKeySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return publickey;
    }

    /**
     * 將Base64轉碼的私鑰串,轉化為私鑰對象
     *
     * @param privateKey
     * @return
     */
    public static PrivateKey createPrivateKey(String privateKey) {
        PrivateKey publickey = null;
        try{
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
            KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());
            publickey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return publickey;
    }
}
View Code

 

1.1、SM2

代碼:

public class BcSm2Util {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 根據publicKey對原始數據data,使用SM2加密
     */
    public static byte[] encrypt(byte[] data, PublicKey publicKey) {
        ECPublicKeyParameters localECPublicKeyParameters = null;

        if (publicKey instanceof BCECPublicKey) {
            BCECPublicKey localECPublicKey = (BCECPublicKey) publicKey;
            ECParameterSpec localECParameterSpec = localECPublicKey.getParameters();
            ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(),
                    localECParameterSpec.getG(), localECParameterSpec.getN());
            localECPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), localECDomainParameters);
        }
        SM2Engine localSM2Engine = new SM2Engine();
        localSM2Engine.init(true, new ParametersWithRandom(localECPublicKeyParameters, new SecureRandom()));
        byte[] arrayOfByte2;
        try {
            arrayOfByte2 = localSM2Engine.processBlock(data, 0, data.length);
            return arrayOfByte2;
        } catch (InvalidCipherTextException e) {

            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根據privateKey對加密數據encodedata,使用SM2解密
     */
    public static byte[] decrypt(byte[] encodedata, PrivateKey privateKey) {
        SM2Engine localSM2Engine = new SM2Engine();
        BCECPrivateKey sm2PriK = (BCECPrivateKey) privateKey;
        ECParameterSpec localECParameterSpec = sm2PriK.getParameters();
        ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(),
                localECParameterSpec.getG(), localECParameterSpec.getN());
        ECPrivateKeyParameters localECPrivateKeyParameters = new ECPrivateKeyParameters(sm2PriK.getD(),
                localECDomainParameters);
        localSM2Engine.init(false, localECPrivateKeyParameters);
        try {
            byte[] arrayOfByte3 = localSM2Engine.processBlock(encodedata, 0, encodedata.length);
            return arrayOfByte3;
        } catch (InvalidCipherTextException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 私鑰簽名
     */
    public static byte[] signByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);
        sig.initSign(privateKey);
        sig.update(data);
        byte[] ret = sig.sign();
        return ret;
    }

    /**
     * 公鑰驗簽
     */
    public static boolean verifyByPublicKey(byte[] data, PublicKey publicKey, byte[] signature) throws Exception {
        Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);
        sig.initVerify(publicKey);
        sig.update(data);
        boolean ret = sig.verify(signature);
        return ret;
    }
}

 

測試

public class BcSm2UtilTest {
    private String test = "woshi測試數據。。..";

    java.security.PublicKey publicKey = null;
    java.security.PrivateKey privateKey = null;

    @Before
    public void setup() throws Exception {//生成公私鑰對
        String[] keys = KeyUtils.generateSmKey();

        System.out.println("原始數據:" + test);
        System.out.println("公鑰:" + new String(keys[0]));
        System.out.println();
        publicKey = KeyUtils.createPublicKey(keys[0]);

        System.out.println("私鑰:" + new String(keys[1]));
        System.out.println();
        privateKey = KeyUtils.createPrivateKey(keys[1]);
    }

    @Test
    public void encrypt() throws Exception {
        byte[] encrypt = BcSm2Util.encrypt(test.getBytes(), publicKey);
        String encryptBase64Str = Base64.getEncoder().encodeToString(encrypt);
        System.out.println("加密數據:" + encryptBase64Str);

        byte[] decrypt = BcSm2Util.decrypt(encrypt, privateKey);

        System.out.println("解密數據:"+new String(decrypt));

        byte[] sign = BcSm2Util.signByPrivateKey(test.getBytes(), privateKey);
        System.out.println("數據簽名:"+ Base64.getEncoder().encodeToString(sign));

        boolean b = BcSm2Util.verifyByPublicKey(test.getBytes(), publicKey,sign);
        System.out.println("數據驗簽:"+ b);
    }
}

 

1.2、SM3 hash及hmac

public class BcSm3Util {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public static byte[] sm3(byte[] srcData) {
        SM3Digest sm3Digest = new SM3Digest();
        sm3Digest.update(srcData, 0, srcData.length);
        byte[] hash = new byte[sm3Digest.getDigestSize()];
        sm3Digest.doFinal(hash, 0);
        return hash;
    }

    public static String sm3Hex(byte[] srcData) {
        byte[] hash = sm3(srcData);
        String hexString = org.apache.commons.codec.binary.Hex.encodeHexString(hash);
        return hexString;
    }

    public static byte[] hmacSm3(byte[] key, byte[] srcData) {
        KeyParameter keyParameter = new KeyParameter(key);
        SM3Digest digest = new SM3Digest();
        HMac mac = new HMac(digest);
        mac.init(keyParameter);
        mac.update(srcData, 0, srcData.length);
        byte[] hash = new byte[mac.getMacSize()];
        mac.doFinal(hash, 0);
        return hash;
    }

    public static String hmacSm3Hex(byte[] key, byte[] srcData) {
        byte[] hash = hmacSm3(key, srcData);
        String hexString = org.apache.commons.codec.binary.Hex.encodeHexString(hash);
        return hexString;
    }

    public static byte[] sm3bc(byte[] srcData) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance("SM3", "BC");
        byte[] digest = messageDigest.digest(srcData);
        return digest;
    }

    public static String sm3bcHex(byte[] srcData) throws Exception {
        byte[] hash = sm3bc(srcData);
        String hexString = org.apache.commons.codec.binary.Hex.encodeHexString(hash);
        return hexString;
    }
}

 

測試

public class BcSm3UtilTest {
    private String test="woshi測試數據。。..";

    @Test
    public void sm3() throws Exception {
        String s = BcSm3Util.sm3Hex(test.getBytes());
        System.out.println(s);
        String s2 = BcSm3Util.sm3bcHex(test.getBytes());
        System.out.println(s2);
        Assert.assertEquals(s,s2);
    }
    
    @Test
    public void hmacSm3Hex() {
        String s = BcSm3Util.hmacSm3Hex("AAAA".getBytes(),test.getBytes());
        System.out.println(s);
    }
}

 

1.4、SM4

public class BcSm4Util {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public static final String ALGORITHM_NAME = "SM4";
    public static final String DEFAULT_KEY = "random_seed";
    // 128-32位16進制;256-64位16進制
    public static final int DEFAULT_KEY_SIZE = 128;


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

    public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {
        return generateKey(DEFAULT_KEY, DEFAULT_KEY_SIZE);
    }

    public static byte[] generateKey(String seed) throws NoSuchAlgorithmException, NoSuchProviderException {
        return generateKey(seed, DEFAULT_KEY_SIZE);
    }

    public static byte[] generateKey(String seed, int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        if (null != seed && !"".equals(seed)) {
            random.setSeed(seed.getBytes());
        }
        kg.init(keySize, random);
        return kg.generateKey().getEncoded();
    }

    /**
     * @description 加密
     */
    public static byte[] encrypt(String algorithmName, byte[] key, byte[] iv, byte[] data) throws Exception {
        return sm4core(algorithmName,Cipher.ENCRYPT_MODE, key, iv, data);
    }

    /**
     * @description 解密
     */
    public static byte[] decrypt(String algorithmName, byte[] key, byte[] iv, byte[] data) throws Exception {
        return sm4core(algorithmName, Cipher.DECRYPT_MODE, key, iv, data);
    }

    private static byte[] sm4core(String algorithmName, int type, byte[] key, byte[] iv, byte[] data) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        if (algorithmName.contains("/ECB/")) {
            cipher.init(type, sm4Key);
        } else {
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            cipher.init(type, sm4Key, ivParameterSpec);
        }

        return cipher.doFinal(data);
    }
}

 

測試

public class BcSm4UtilTest {
    byte[] key = BcSm4Util.generateKey();
    byte[] iv = null;

    String text = "我是加密數據,請測試。。8888";

    public BcSm4UtilTest() throws NoSuchProviderException, NoSuchAlgorithmException {
    }

    @Test
    public void bcSm4UtilTest() throws Exception {
        List<String> algorithm = new ArrayList<>();
        algorithm.add(("SM4/ECB/NOPADDING"));
        algorithm.add(("SM4/ECB/PKCS5PADDING"));
        algorithm.add(("SM4/ECB/ISO10126PADDING"));
        algorithm.add(("SM4/CBC/NOPADDING"));
        algorithm.add(("SM4/CBC/PKCS5PADDING"));
        algorithm.add(("SM4/CBC/ISO10126PADDING"));
        algorithm.add(("SM4/PCBC/NOPADDING"));
        algorithm.add(("SM4/PCBC/PKCS5PADDING"));
        algorithm.add(("SM4/PCBC/ISO10126PADDING"));
        algorithm.add(("SM4/CTR/NOPADDING"));
        algorithm.add(("SM4/CTR/PKCS5PADDING"));
        algorithm.add(("SM4/CTR/ISO10126PADDING"));
        algorithm.add(("SM4/CTS/NOPADDING"));
        algorithm.add(("SM4/CTS/PKCS5PADDING"));
        algorithm.add(("SM4/CTS/ISO10126PADDING"));
        if (iv == null)
            iv = AbstractSymmetric.initIv(16);

        for (String s : algorithm) {
            //SM4加密
            try {
                System.out.println("SM4加密算法: " + s);
                System.out.println("SM4加密原始數據: " + text);
                System.out.println("SM4加密key: " + Base64.getEncoder().encodeToString(key));
                System.out.println("SM4加密iv: " + Base64.getEncoder().encodeToString(iv));

                byte[] encrypt = BcSm4Util.encrypt(s, key, iv, text.getBytes());
                System.out.println("SM4加密數據密文: " + Base64.getEncoder().encodeToString(encrypt));

                //SM4解密
                byte[] decrypt = BcSm4Util.decrypt(s, key, iv, encrypt);
                System.out.println("SM4解密數據: " + new String(decrypt));
            } catch (Exception e) {
                if (e instanceof IllegalBlockSizeException) {
                    System.err.println("SM4解密數據:算法 " + s + "數據需自己手工對齊");
                } else {
                    System.err.println("SM4解密數據:算法 " + s +"::"+ e.getMessage());
                }
            } finally {
                System.err.println("---------------------------------------");
                TimeUnit.SECONDS.sleep(1);
            }
        }
    }
}

 

 

 

水電費


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM