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; } }
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); } } } }
水电费