赞
踩
数据的加解密有很多种方式,几种常用的加密算法如下:
DES(Data Encryption Standard):对称算法,数据加密标准,速度较快,适用于加密大量数据的场合;
3DES(Triple DES):是基于DES的对称算法,对一块数据用三个不同的密钥进行三次加密,强度更高;
RC2和RC4:对称算法,用变长密钥对大量数据进行加密,比 DES 快;
IDEA(International Data Encryption Algorithm)国际数据加密算法,使用 128 位密钥提供非常强的安全性;
RSA:由 RSA 公司发明,是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变的,非对称算法
本文着重介绍SM2国密算法的使用,这是一款中国国家密码局发布的一款加密算法,本文介绍使用语言环境为java语言
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency>
主要是引入了工具包和lombok依赖
package com.hl.sm2demo.util; import lombok.extern.slf4j.Slf4j; 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; import java.util.Map; /** * @ Description 国密公私钥对工具类 */ @Slf4j public class KeyUtils { public static final String PUBLIC_KEY = "publicKey"; public static final String PRIVATE_KEY = "privateKey"; /** * 生成国密公私钥对 */ public static Map<String, 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 publicKeyStr = new String(Base64.getEncoder().encode(publicKey.getEncoded())); String privateKeyStr = new String(Base64.getEncoder().encode(privateKey.getEncoded())); return Map.of(PUBLIC_KEY, publicKeyStr, PRIVATE_KEY, privateKeyStr); } /** * 将Base64转码的公钥串,转化为公钥对象 */ 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) { log.error("将Base64转码的公钥串,转化为公钥对象异常:{}", e.getMessage(), e); } return publickey; } /** * 将Base64转码的私钥串,转化为私钥对象 */ 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) { log.error("将Base64转码的私钥串,转化为私钥对象异常:{}", e.getMessage(), e); } return publickey; } }
生成公私钥字符串map对象,公钥串转公钥对象方法和私钥串转私钥对象方法
package com.hl.sm2demo.util; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.asn1.gm.GMObjectIdentifiers; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; import java.security.*; /** * @ Description SM2实现工具类 */ @Slf4j public class Sm2Util {/* 这行代码是在Java中用于向安全系统添加Bouncy Castle安全提供器的。 Bouncy Castle是一个流行的开源加密库,它提供了许多密码学算法和安全协议的实现。 通过调用Security.addProvider并传入BouncyCastleProvider对象,你可以注册Bouncy Castle提供的安全服务和算法到Java的安全框架中。 这样一来,你就可以在你的应用程序中使用Bouncy Castle所提供的加密算法、密钥生成和管理等功能。*/ static { Security.addProvider(new BouncyCastleProvider()); } /** * 根据publicKey对原始数据data,使用SM2加密 */ public static byte[] encrypt(byte[] data, PublicKey publicKey) { ECPublicKeyParameters localECPublicKeyParameters = getEcPublicKeyParameters(publicKey); 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) { log.error("SM2加密失败:{}", e.getMessage(), e); return null; } } private static ECPublicKeyParameters getEcPublicKeyParameters(PublicKey publicKey) { ECPublicKeyParameters localECPublicKeyParameters = null; if (publicKey instanceof BCECPublicKey localECPublicKey) { ECParameterSpec localECParameterSpec = localECPublicKey.getParameters(); ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(), localECParameterSpec.getG(), localECParameterSpec.getN()); localECPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), localECDomainParameters); } return localECPublicKeyParameters; } /** * 根据privateKey对加密数据encode data,使用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 { return localSM2Engine.processBlock(encodeData, 0, encodeData.length); } catch (InvalidCipherTextException e) { log.error("SM2解密失败:{}", e.getMessage(), e); 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); return sig.sign(); } /** * 公钥验签 */ 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); return sig.verify(signature); } }
package com.hl.sm2demo; import com.hl.sm2demo.util.KeyUtils; import com.hl.sm2demo.util.Sm2Util; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Base64; import java.util.Map; @SpringBootTest class Sm2DemoApplicationTests { PublicKey publicKey = null; PrivateKey privateKey = null; @Test public void test() throws Exception { //生成公私钥对 Map<String,String> keys = KeyUtils.generateSmKey(); String testStr = "hello JAVA"; System.out.println("原始字符串:" + testStr); System.out.println("公钥:" + keys.get(KeyUtils.PUBLIC_KEY)); publicKey = KeyUtils.createPublicKey(keys.get(KeyUtils.PUBLIC_KEY)); System.out.println("私钥:" + keys.get(KeyUtils.PRIVATE_KEY)); privateKey = KeyUtils.createPrivateKey(keys.get(KeyUtils.PRIVATE_KEY)); System.out.println(); //公钥加密 byte[] encrypt = Sm2Util.encrypt(testStr.getBytes(), publicKey); //加密转base64 String encryptBase64Str = Base64.getEncoder().encodeToString(encrypt); System.out.println("加密数据:" + encryptBase64Str); //私钥签名,方便对方收到数据后用公钥验签 byte[] sign = Sm2Util.signByPrivateKey(testStr.getBytes(), privateKey); System.out.println("数据签名:" + Base64.getEncoder().encodeToString(sign)); //公钥验签,验证通过后再进行数据解密 boolean b = Sm2Util.verifyByPublicKey(testStr.getBytes(), publicKey, sign); System.out.println("数据验签:" + b); //私钥解密 byte[] decode = Base64.getDecoder().decode(encryptBase64Str); byte[] decrypt = Sm2Util.decrypt(decode, privateKey); assert decrypt != null; System.out.println("解密数据:" + new String(decrypt)); } }
测试代码主要逻辑:
密钥准备:首先生成一个公私钥对字符串,再使用工具分别转换为公私钥的java对象;
加密过程:把数据使用公钥加密,把密文转换成base64编码格式,再用私钥签名;
解密过程:拿到加密数据后用公钥验签,以确保密文数据没有被第三方拦截篡改,验证通过后,base64解码,最后使用私钥进行解密,还原数据
测试结果如下图:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。