Java使用国密算法

Java使用国密算法

什么是国密算法

国密算法是指 SM2 SM3 SM4 这3套含数据对称加解密,数据签名,数据非对称加解密功能的数据加密算法。其中 SM4 算法用于数据对称加密和解密;SM3算法用于计算数据的摘要签名;SM2算法用于数据 非对称加密和解密。
在政务行业的一些政务项目或产品中,会要求使用国密算法来替代 RSA,MD5,DES等算法。

在Java项目中使用国密算法

引入相关依赖

这里我使用的是jdk1.8 的maven项目,需要在pom.xml里引入以下依赖

        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.7.8version>
        dependency>
        <dependency>
            <groupId>org.bouncycastlegroupId>
            <artifactId>bcprov-jdk15onartifactId>
            <version>1.69version>
        dependency>

Java使用SM4算法对称加密解密

package cn.demo.sm;


import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;

public class SM4Test {
    /**
     * sm4 对称加密 ,需要1个加密key
     * @param args
     */
    public static void main(String[] args) {
        //SM4 requires a 128 bit key
        //需要一个长度为16的字符串 16*8=128 bit
        String key = RandomUtil.randomString(16);
        System.err.println("生成1个128bit的加密key:"+key);

        //原文
        String str = "hello";
        System.err.println("原文:"+str);

        StopWatch sw = StopWatch.create("q11");
        sw.start();

        SM4 sm41 = SmUtil.sm4(key.getBytes());
        //加密为Hex
        String hexPass = sm41.encryptHex(str);
        System.err.println("Hex形式的密文:"+hexPass);
        sw.stop();
        System.err.println(sw.getLastTaskInfo().getTimeSeconds());

        sw.start();
        //加密为base64
        String base64Pass = sm41.encryptBase64(str);
        System.err.println("base64形式的密文:"+base64Pass);
        sw.stop();
        System.err.println(sw.getLastTaskInfo().getTimeSeconds());

        System.err.println("--------------");
        //hex解密
        String s = sm41.decryptStr(hexPass);
        System.out.println(s);

        System.out.println("--------------");
        //base64解密
        String s2 = sm41.decryptStr(base64Pass);
        System.out.println(s2);
    }
}

测试结果

生成1个128bit的加密key:jsimjrby3wqb7dbq
原文:hello
Hex形式的密文:a18a4bbea96ca3103192d20650f8f190
1.19635956
base64形式的密文:oYpLvqlsoxAxktIGUPjxkA==
0.001947113
--------------
hello
--------------
hello

可以看出SM4加密需要1个128bit的加密key ,后面进行加密和解密都需要这个key。

SM4加密时有2种密文形式,一种是hex,一种是base64。

经测试发现SM4返回Hex形式的密文更耗时,所以建议开发中使用SM4返回base64形式的密文。

Java使用SM3算法计算数据的摘要签名

package cn.demo.sm;
import cn.hutool.crypto.SmUtil;

public class SM3Test {
    /**
     * sm3 摘要签名算法
     * @param args
     */
    public static void main(String[] args) {
        String str = "hello2023";
        /**
         * str重复执行sm3签名后的值 是相同的
         */
        String s = SmUtil.sm3(str);
        System.out.println(s);
    }
   
}    

测试结果

69f5c5c5413eaf9543b1e35ce6aa60d0eab217764e3f9d621e30785c8471e08f

可以看出SM3摘要签名算法 是可以重复执行,且多次执行结果一致的。

需要注意的是SM3 算法是不可逆的,只能从原文得到摘要签名,不能从摘要签名 反向得到原文,

这一点和MD5算法是相似的。

Java使用SM2算法进行非对称加密解密

package cn.demo.sm;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.*;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class SM2Utils {

    static final BouncyCastleProvider bc = new BouncyCastleProvider();

    public static Map<String,Object> generateKey(){
        KeyPair pair = SecureUtil.generateKeyPair("SM2");
        Map<String,Object> map = new HashMap<>();
        map.put("publicKey",pair.getPublic());
        map.put("privateKey",pair.getPrivate());
        return map;
    }

    public static String encrypt(String body, PublicKey aPublic){
        SM2 sm2 = SmUtil.sm2();
        sm2.setPublicKey(aPublic);
        String s = sm2.encryptBcd(body, KeyType.PublicKey);
        return s;
    }

    public static String decrypt(String data, PrivateKey privateKey){
        SM2 sm2obj = SmUtil.sm2();
        sm2obj.setPrivateKey(privateKey);
        String decStr = StrUtil.utf8Str(sm2obj.decryptFromBcd(data,KeyType.PrivateKey));
        return decStr;
    }


    /**
     * 从字符串中读取 私钥 key
     * @param privateKeyStr String
     * @return PrivateKey
     */
    public static PrivateKey strToPrivateKey(String privateKeyStr){
        PrivateKey privateKey = null;
        try {
            byte[] encPriv = Base64.decode(privateKeyStr);
            KeyFactory keyFact = KeyFactory.getInstance("EC", bc);
            privateKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(encPriv));

        }catch (Exception e){
            e.printStackTrace();
        }
        return privateKey;
    }

    /**
     * 从字符串中读取 公钥 key
     * @param publicKeyStr String
     * @return PublicKey
     */
    public  static PublicKey strToPublicKey(String publicKeyStr){
        PublicKey publicKey =  null;
        try {
            byte[] encPub = Base64.decode(publicKeyStr);
            KeyFactory keyFact = KeyFactory.getInstance("EC", bc);
            publicKey = keyFact.generatePublic(new X509EncodedKeySpec(encPub));
        }catch (Exception e){
            e.printStackTrace();
        }
        return publicKey;
    }


    /**
     * 公钥 key  转文件
     * @param publicKey PublicKey
     * @param path String
     */
    public static void exportPublicKey(PublicKey publicKey,String path){
        File file = new File(path);
        try {
            if (!file.exists()){
                file.createNewFile();
            }
            byte[] encPub = publicKey.getEncoded();
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(encPub);
            fos.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 私钥 key 转文件
     * @param privateKey PrivateKey
     * @param keyPath String
     */
    public static void exportPrivateKey(PrivateKey privateKey, String keyPath){
        File file = new File(keyPath);
        try {
            if (!file.exists()){
                file.createNewFile();
            }
            byte[]  encPriv = privateKey.getEncoded();
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(encPriv);
            fos.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }


    public static PublicKey importPublicKey(String path){
        File file = new File(path);
        try {
            if (!file.exists()){
                return null;
            }
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[16];
            int size;
            while ((size = fis.read(buffer)) != -1){
                baos.write(buffer,0,size);
            }
            fis.close();
            byte[] bytes = baos.toByteArray();
            String publicKeyStr = Base64.encode(bytes);

            return strToPublicKey(publicKeyStr);
        }catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }


    public static PrivateKey importPrivateKey(String keyPath){
        File file = new File(keyPath);
        try {
            if (!file.exists()){
                return null;
            }
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte buffer[] = new byte[16];
            int size;
            while ((size = fis.read(buffer)) != -1){
                baos.write(buffer,0,size);
            }
            fis.close();

            byte[]  bytes = baos.toByteArray();
            String privateKeyStr = Base64.encode(bytes);

            return strToPrivateKey(privateKeyStr);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
package cn.demo.sm;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;

public class SM2Test {

    public static void test1(){
        String text = "测试aaaaaaaaa";

        SM2 sm2 = SmUtil.sm2();

        //第一种 使用随机密钥对
        String s = sm2.encryptBcd(text, KeyType.PublicKey);
        System.out.println(s);
        String s1 = StrUtil.utf8Str(sm2.decryptFromBcd(s, KeyType.PrivateKey));
        System.out.println(s1);
    }

    public static void test2(){
        String text = "测试aaaaaaaaa";

        System.err.println(text);

        //第二种  使用自定义密钥对
        KeyPair keyPair = SecureUtil.generateKeyPair("SM2");
        byte[] priKey = keyPair.getPrivate().getEncoded();
        byte[] pubKey = keyPair.getPublic().getEncoded();

        System.err.println("=====================================");
        SM2 sm2obj = SmUtil.sm2(priKey,pubKey);
        //公钥加密,私钥解密
        String encStr = sm2obj.encryptBcd(text,KeyType.PublicKey);
        System.err.println(encStr);

        String decStr = StrUtil.utf8Str(sm2obj.decryptFromBcd(encStr,KeyType.PrivateKey));
        System.err.println(decStr);
    }


    public static void main(String[] args) {
        //第一种 使用随机密钥对
//        test1();

        //第二种  使用自定义密钥对
//        test2();

        //第三种 生成pem文件,从pem文件里 读取 公钥 和 私钥,再进行 公钥加密,私钥解密
        test3();
    }

    /**
     * SM2非对称加密
     */
    public static void test3() {
        String text = "测试aaaaaaaaa";
        System.err.println(text);

        KeyPair pair = SecureUtil.generateKeyPair("SM2");

        PublicKey aPublic = pair.getPublic();
        PrivateKey aPrivate = pair.getPrivate();
        //公钥 key 和私钥 key 转文件
        SM2Utils.exportPublicKey(aPublic,"F:/sm2/public_key.pem");
        SM2Utils.exportPrivateKey(aPrivate,"F:/sm2/private_key.pem");

        //从pem文件里 读取 公钥 和 私钥
        PublicKey pubk2 = SM2Utils.importPublicKey("F:/sm2/public_key.pem");
        PrivateKey priK2 = SM2Utils.importPrivateKey("F:/sm2/private_key.pem");

        //公钥加密
        SM2 sm2 = SmUtil.sm2();
        sm2.setPublicKey(pubk2);
        String encStr = sm2.encryptBcd(text, KeyType.PublicKey);

        System.err.println(encStr);


        //私钥解密
        SM2 sm2obj = SmUtil.sm2();
        sm2obj.setPrivateKey(priK2);
        String decStr = StrUtil.utf8Str(sm2obj.decryptFromBcd(encStr,KeyType.PrivateKey));

        System.err.println(decStr);

    }

}

测试结果

测试aaaaaaaaa
04A278AAF9E455BAA2B09EA635DA71342ED507CC72F4D8E616D156BF378206CF1BC319146B863DA0C76784B1DC637E72BFA21D98A82169816AEF98B83F0F29AC97AC951FFF72300B64A9D438749C7F6D7AEFC7581D314CB01C9ABA96DD7591E0DF3990069E1BB8E7923B29B0F5FF12AD
测试aaaaaaaaa

SM2算法的加密和解密过程类似于RSA加解密。

可以看出SM2加密需要1个密钥对 ,后面进行加密和解密都需要这个密钥对,

这个密钥对可以分开保存成2个pem文件,

程序员可以从pem文件里 读取 公钥 和 私钥,再进行 公钥加密,私钥解密。

你可能感兴趣的:(JavaSE笔记,java,算法,国密算法,SM3,SM4,SM2)