7、【java数据安全】国家商用密码介绍及对称加密、非对称加密使用示例(一)

java数据安全 系列文章

1、【java数据安全】数据安全之加密解密(base64、MD、SHA、DES、AES、IDEA、PBE、DH、RSA、EIGamal)、数字签名(DSA、ECDSA)和数字证书介绍、应用示例详细介绍
2、【java数据安全】base64与报文摘要MD(md5、sha、mac)简单介绍及应用场景、示例
3、【java数据安全】对称加密的5种(DES/3DES、AES、IDEA、PBE)常见算法的使用示例
4、【java数据安全】非对称加密算法(DH、RSA、EIGamal/DSA)的介绍、应用场景和示例
5、【java数据安全】数字签名的三种算法(RSA、DSA和ECDSA)使用示例
6、【java数据安全】数字信封介绍及实现流程
7、【java数据安全】国家商用密码介绍及对称加密、非对称加密使用示例(一)
8、【java数据安全】国家商用密码介绍及数字签名、密钥交换、密钥编码格式使用示例(二)



本文简单的介绍了国密的内容和对称加密、非对称加密和报文摘要的使用示例。

一、概念介绍

1、总览

在这里插入图片描述

2、介绍

国密即国家密码局认定的国产密码算法,即商用密码。商用密码,是指能够实现商用密码算法的加密、解密和认证等功能的技术。(包括密码算法编程技术和密码算法芯片、加密卡等的实现技术)。
了保障在金融、医疗等领域保障信息传输安全,国家商用密码管理办公室制定了一系列密码标准,包括SM1(SCB2)、SM2、SM3、SM4、SM7、SM9、祖冲之密码算法(ZUC)等。

SSF33、SM1、SM4、SM7是对称算法
SM2、SM9是非对称算法
SM3是哈希算法

3、SM1

SM1 算法是分组对称算法,分组长度为128位,密钥长度都为 128 比特,算法安全保密强度及相关软硬件实现性能与 AES 相当,算法不公开,仅以 IP 核的形式存在于芯片中。

采用该算法已经研制了系列芯片、智能 IC 卡、智能密码钥匙、加密卡、加密机等安全产品,广泛应用于电子政务、电子商务及国民经济的各个应用领域(包括国家政务通、警务通等重要领域)。

SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用

4、SM2

SM2算法是一种先进安全的公钥密码算法,在我们国家商用密码体系中被用来替换RSA算法。SM2算法就是ECC椭圆曲线密码机制,但在签名、密钥交换方面不同于ECDSA、ECDH等国际标准,而是采取了更为安全的机制。另外,SM2推荐了一条256位的曲线作为标准曲线。

SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。

包括SM2-1椭圆曲线数字签名算法,SM2-2椭圆曲线密钥交换协议,SM2-3椭圆曲线公钥加密算法,分别用于实现数字签名密钥协商和数据加密等功能

SM2算法与RSA算法不同的是,SM2算法是基于椭圆曲线上点群离散对数难题,相对于RSA算法,256位的SM2密码强度已经比2048位的RSA密码强度要高。

5、SM3

SM3是一种哈希算法,其算法本质是给数据加一个固定长度的指纹,这个固定长度就是256比特。用于密码应用中的数字签名和验证、消息认证码的生成与验证以及随机数的生成,可满足多种密码应用的安全需求。

SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。

适用于商用密码应用中的数字签名和验证消息认证码的生成与验证以及随机数的生成,可满足多种密码应用的安全需求。为了保证杂凑算法的安全性,其产生的杂凑值的长度不应太短。

6、SM4

SM4算法是一个分组算法,用于无线局域网产品。该算法的分组长度为128比特,密钥长度为128比特。加密算法与密钥扩展算法都采用32轮非线性迭代构。解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。

用于实现数据的加密/解密运算,以保证数据和信息的机密性。要保证一个对称密码算法的安全性的基本条件是其具备足够的密钥长度,SM4算法与AES算法具有相同的密钥长度分组长度128比特,因此在安全性上高于3DES算法。

7、SM7和SM9

SM7算法是一种分组密码算法,分组长度为 128 比特,密钥长度为 128 比特。SM7的算法文本目前没有公开发布。
SM9是基于对的标识密码算法,与SM2类似,包含四个部分:总则,数字签名算法,密钥交换协议以及密钥封装机制和公钥加密算法。在这些算法中使用了椭圆曲线上的对这一个工具,不同于传统意义上的SM2算法,可以实现基于身份的密码体质,也就是公钥与用户的身份信息即标识相关,从而比传统意义上的公钥密码体质有许多优点,省去了证书管理等。

二、实现

本文国密算法实现是基于bouncycastle(版本1.6.0)

1、maven依赖

		<dependency>
			<groupId>org.testng</groupId>
			<artifactId>testng</artifactId>
			<version>6.9.10</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.11</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.60</version>
		</dependency>

2、对称加密

对称加密使用的是SM4
由于是软件应用,硬件应用使用SM1
密钥的长度必须是128位的,其他的长度会报异常。
对称加密使用不同的模式,实现方式不同,本文实现了ECB和CBC两种。

1)、ECB实现示例

1、构建密钥
public static byte[] generateKey(int keySize) throws Exception{
	KeyGenerator kg = KeyGenerator.getInstance(SM4, "BC");
	kg.init(keySize, new SecureRandom());
	return kg.generateKey().getEncoded();
	}
2、加密
//构造加密算法
private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key) throws Exception {
	Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
	Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
	cipher.init(mode, sm4Key);
	return cipher;
	}
//加密
public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
	Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
	return cipher.doFinal(data);
	}
3、解密
public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception {
	Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
	return cipher.doFinal(cipherText);
	}
4、使用示例
public static final byte[] SRC_DATA = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
	// 密钥
	byte[] key = null;
	// 密文
	byte[] encryptECB = null;
	//
	byte[] decryptedECB = null;
	
	@BeforeClass
	public void init() {
		try {
			key = SM4Util.generateKey();
			iv = SM4Util.generateKey();
		} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
			e.printStackTrace();
		}
	}

	// 加密
	@Test
	public void testECBEnrypt() {
		try {
			encryptECB = SM4Util.encrypt_Ecb_Padding(key, SRC_DATA);
		} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException
				| IllegalBlockSizeException | BadPaddingException e) {
			e.printStackTrace();
		}
		System.out.println("SM4 ECB 加密结果:" + Arrays.toString(encryptECB));
	}

	// 解密
	@Test(dependsOnMethods = { "testECBEnrypt" })
	public void testECBDecrypt() {
		try {
			decryptedECB = SM4Util.decrypt_Ecb_Padding(key, encryptECB);
		} catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException
				| NoSuchProviderException | NoSuchPaddingException e) {
			e.printStackTrace();
		}
		System.out.println("SM4 ECB 解密结果:" + Arrays.toString(decryptedECB));
		if (!Arrays.equals(decryptedECB, SRC_DATA)) {
			Assert.fail();
		}
	}

2)、CBC

1、构建密钥
public static byte[] generateKey(int keySize) throws Exception{
	KeyGenerator kg = KeyGenerator.getInstance(SM4, "BC");
	kg.init(keySize, new SecureRandom());
	return kg.generateKey().getEncoded();
	}
2、加密
//构建加密算法
private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv)
			throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
			NoSuchProviderException, NoSuchPaddingException {
		Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
		Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
		IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
		cipher.init(mode, sm4Key, ivParameterSpec);
		return cipher;
	}
//加密
public static byte[] encrypt_Cbc_Padding(byte[] key, byte[] iv, byte[] data)
			throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
			IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
		Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv);
		return cipher.doFinal(data);
	}
3、解密
public static byte[] decrypt_Cbc_Padding(byte[] key, byte[] iv, byte[] cipherText)
			throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
			NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException {
		Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv);
		return cipher.doFinal(cipherText);
	}
4、使用示例
public static final byte[] SRC_DATA = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
	// 密钥
	byte[] key = null;
	// 初始化向量(?)
	byte[] iv = null;
	// 密文
	byte[] encryptCBC = null;
	//
	byte[] decryptedCBC = null;

	@BeforeClass
	public void init() {
		try {
			key = SM4Util.generateKey();
			iv = SM4Util.generateKey();
		} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
			e.printStackTrace();
		}
	}
	@Test
	public void testCBCEncrypt() {
		try {
			encryptCBC = SM4Util.encrypt_Cbc_Padding(key, iv, SRC_DATA);
		} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException
				| IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		}
		System.out.println("SM4 CBC 加密结果:" + Arrays.toString(encryptCBC));
	}

	@Test(dependsOnMethods = { "testCBCEncrypt" })
	public void testCBCDecrypt() {
		try {
			decryptedCBC = SM4Util.decrypt_Cbc_Padding(key, iv, encryptCBC);
		} catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException
				| NoSuchProviderException | NoSuchPaddingException | InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		}
		System.out.println("SM4 CBC 解密结果:" + Arrays.toString(decryptedCBC));
		if (!Arrays.equals(decryptedCBC, SRC_DATA)) {
			Assert.fail();
		}
	}

3)、完整示例

1、帮助类
import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * @author alan
 * 2018年12月11日
 */
public class BaseUtil {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
}
2、实现类
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.win.security.sm.BaseUtil;

/**
 * @author alan 2018年12月11日
 */
public class SM4Util extends BaseUtil {

	public static final String ALGORITHM_NAME = "SM4";

	public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
	public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";

	public static final int DEFAULT_KEY_SIZE = 128;

	/**
	 * 使用默认密钥长度生成密钥
	 * 
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 */
	public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {
		return generateKey(DEFAULT_KEY_SIZE);
	}

	/**
	 * 获取密钥,自己设置密钥长度<br>
	 * 疑问:sm4密钥的长度是固定的128位,如次设置有什么作用? 密钥长度设置为其他值,则报异常:SM4 requires a 128 bit key
	 * 
	 * @param keySize
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 */
	public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
		KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
		kg.init(keySize, new SecureRandom());
		return kg.generateKey().getEncoded();
	}

	/**
	 * ECB加密算法初始化
	 * 
	 * @param algorithmName
	 *            算法名称
	 * @param mode
	 *            模式
	 * @param key
	 *            密钥
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 */
	private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key)
			throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException {
		Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
		Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
		cipher.init(mode, sm4Key);
		return cipher;
	}

	/**
	 * 使用ecb模式进行加密
	 * 
	 * @param key
	 *            密钥
	 * @param data
	 *            待加密数组
	 * @return 加密后的密文数组
	 * @throws InvalidKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws NoSuchPaddingException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data)
			throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
			IllegalBlockSizeException, BadPaddingException {
		Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
		return cipher.doFinal(data);
	}

	/**
	 * 使用ECB模式进行解密
	 * 
	 * @param key
	 *            密钥
	 * @param cipherText
	 *            密文
	 * @return 明文
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws InvalidKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws NoSuchPaddingException
	 */
	public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText)
			throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
			NoSuchProviderException, NoSuchPaddingException {
		Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
		return cipher.doFinal(cipherText);
	}

	/**
	 * CBC加密算法初始化
	 * 
	 * @param algorithmName
	 *            算法名称
	 * @param mode
	 *            模式
	 * @param key
	 *            密钥
	 * @param iv
	 *            初始化向量
	 * @return
	 * @throws InvalidKeyException
	 * @throws InvalidAlgorithmParameterException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws NoSuchPaddingException
	 */
	private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv)
			throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
			NoSuchProviderException, NoSuchPaddingException {
		Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
		Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
		IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
		cipher.init(mode, sm4Key, ivParameterSpec);
		return cipher;
	}

	/**
	 * 使用CBC模式进行加密
	 * 
	 * @param key
	 *            密钥
	 * @param iv
	 *            初始化向量
	 * @param data
	 *            待加密的明文
	 * @return 加密后的密文
	 * @throws InvalidKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws NoSuchPaddingException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws InvalidAlgorithmParameterException
	 */
	public static byte[] encrypt_Cbc_Padding(byte[] key, byte[] iv, byte[] data)
			throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
			IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
		Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv);
		return cipher.doFinal(data);
	}

	/**
	 * 使用CBC模式进行解密
	 * 
	 * @param key
	 *            密钥
	 * @param iv
	 * @param cipherText
	 *            明文
	 * @return 明文
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws InvalidKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws NoSuchPaddingException
	 * @throws InvalidAlgorithmParameterException
	 */
	public static byte[] decrypt_Cbc_Padding(byte[] key, byte[] iv, byte[] cipherText)
			throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
			NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException {
		Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv);
		return cipher.doFinal(cipherText);
	}

}
3、测试类
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

/**
 * @author alan 2018年12月11日
 */
public class SM4UtilTest {
	public static final byte[] SRC_DATA = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
	// 密钥
	byte[] key = null;
	// 初始化向量(?)
	byte[] iv = null;
	// 密文
	byte[] encryptECB = null;
	byte[] encryptCBC = null;
	//
	byte[] decryptedECB = null;
	byte[] decryptedCBC = null;

	@BeforeClass
	public void init() {
		try {
			key = SM4Util.generateKey();
			iv = SM4Util.generateKey();
		} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
			e.printStackTrace();
		}
	}

	// 加密
	@Test
	public void testECBEnrypt() {
		try {
			encryptECB = SM4Util.encrypt_Ecb_Padding(key, SRC_DATA);
		} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException
				| IllegalBlockSizeException | BadPaddingException e) {
			e.printStackTrace();
		}
		System.out.println("SM4 ECB 加密结果:" + Arrays.toString(encryptECB));
	}

	// 解密
	@Test(dependsOnMethods = { "testECBEnrypt" })
	public void testECBDecrypt() {
		try {
			decryptedECB = SM4Util.decrypt_Ecb_Padding(key, encryptECB);
		} catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException
				| NoSuchProviderException | NoSuchPaddingException e) {
			e.printStackTrace();
		}
		System.out.println("SM4 ECB 解密结果:" + Arrays.toString(decryptedECB));
		if (!Arrays.equals(decryptedECB, SRC_DATA)) {
			Assert.fail();
		}
	}

	@Test
	public void testCBCEncrypt() {
		try {
			encryptCBC = SM4Util.encrypt_Cbc_Padding(key, iv, SRC_DATA);
		} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException
				| IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		}
		System.out.println("SM4 CBC 加密结果:" + Arrays.toString(encryptCBC));
	}

	@Test(dependsOnMethods = { "testCBCEncrypt" })
	public void testCBCDecrypt() {
		try {
			decryptedCBC = SM4Util.decrypt_Cbc_Padding(key, iv, encryptCBC);
		} catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException
				| NoSuchProviderException | NoSuchPaddingException | InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		}
		System.out.println("SM4 CBC 解密结果:" + Arrays.toString(decryptedCBC));
		if (!Arrays.equals(decryptedCBC, SRC_DATA)) {
			Assert.fail();
		}
	}
}

3、非对称加密

非对称加密采用的是SM2

1)、构建密钥对

public static AsymmetricCipherKeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random) {
	ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParameters, random);
	ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
	keyGen.init(keyGenerationParams);
	return keyGen.generateKeyPair();
}

2)、加密

public static byte[] encrypt(ECPublicKeyParameters pubKey, byte[] srcData) throws InvalidCipherTextException {
	SM2Engine engine = new SM2Engine();
	ParametersWithRandom pwr = new ParametersWithRandom(pubKey, new SecureRandom());
	engine.init(true, pwr);
	return engine.processBlock(srcData, 0, srcData.length);
}

3)、解密

public static byte[] decrypt(ECPrivateKeyParameters priKey, byte[] encryptedData)
		throws InvalidCipherTextException {
	SM2Engine engine = new SM2Engine();
	engine.init(false, priKey);
	return engine.processBlock(encryptedData, 0, encryptedData.length);
}

4)、使用示例

AsymmetricCipherKeyPair keyPair = SM2Coder.generateKeyPair();
			ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate();
			ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic();

			byte[] encryptedData = SM2Coder.encrypt(pubKey, SRC_DATA);
			System.out.println("SM2 encrypt result:\n" + ByteUtils.toHexString(encryptedData));
			byte[] decryptedData = SM2Coder.decrypt(priKey, encryptedData);
			System.out.println("SM2 decrypt result:\n" + ByteUtils.toHexString(decryptedData));

(5)、密钥恢复
密钥恢复是在知道构建椭圆曲线的一些参数的情况下构建公钥。

String priHex = "5DD701828C424B84C5D56770ECF7C4FE882E654CAC53C7CC89A66B1709068B9D";
			String xHex = "FF6712D3A7FC0D1B9E01FF471A87EA87525E47C7775039D19304E554DEFE0913";
			String yHex = "F632025F692776D4C13470ECA36AC85D560E794E1BCCF53D82C015988E0EB956";
			String encodedPubHex = "04FF6712D3A7FC0D1B9E01FF471A87EA87525E47C7775039D19304E554DEFE0913F632025F692776D4C13470ECA36AC85D560E794E1BCCF53D82C015988E0EB956";
			String signHex = "30450220213C6CD6EBD6A4D5C2D0AB38E29D441836D1457A8118D34864C247D727831962022100D9248480342AC8513CCDF0F89A2250DC8F6EB4F2471E144E9A812E0AF497F801";
			byte[] signBytes = ByteUtils.fromHexString(signHex);
			byte[] src = ByteUtils.fromHexString("0102030405060708010203040506070801020304050607080102030405060708");
			byte[] withId = ByteUtils.fromHexString("31323334353637383132333435363738");

			ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(new BigInteger(ByteUtils.fromHexString(priHex)),
					SM2Coder.DOMAIN_PARAMS);
			ECPublicKeyParameters pubKey = SM2Coder.createECPublicKeyParameters(xHex, yHex, SM2Coder.CURVE,
					SM2Coder.DOMAIN_PARAMS);

			if (!SM2Coder.verify(pubKey, src, signBytes)) {
				Assert.fail("verify failed");
			}

5)、完整示例

1、实现类
import java.math.BigInteger;
import java.security.SecureRandom;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithID;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import com.win.security.sm.BaseUtil;

/**
 * @author alan 2018年12月11日
 */
public class SM2Coder extends BaseUtil {
	//
	/*
	 * 以下为SM2推荐曲线参数
	 */
	public static final SM2P256V1Curve CURVE = new SM2P256V1Curve();
	public final static BigInteger SM2_ECC_N = CURVE.getOrder();
	public final static BigInteger SM2_ECC_H = CURVE.getCofactor();
	public final static BigInteger SM2_ECC_GX = new BigInteger(
			"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
	public final static BigInteger SM2_ECC_GY = new BigInteger(
			"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
	public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY);
	public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, SM2_ECC_N, SM2_ECC_H);

	//
	/**
	 * 生成ECC密钥对
	 *
	 * @return ECC密钥对
	 */
	public static AsymmetricCipherKeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random) {
		ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParameters, random);
		ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
		keyGen.init(keyGenerationParams);
		return keyGen.generateKeyPair();
	}

	/**
	 * @return
	 */
	public static AsymmetricCipherKeyPair generateKeyPair() {
		SecureRandom random = new SecureRandom();
		return generateKeyPair(DOMAIN_PARAMS, random);
	}

	/**
	 * @param pubKey
	 * @param srcData
	 * @return
	 * @throws InvalidCipherTextException
	 */
	public static byte[] encrypt(ECPublicKeyParameters pubKey, byte[] srcData) throws InvalidCipherTextException {
		SM2Engine engine = new SM2Engine();
		ParametersWithRandom pwr = new ParametersWithRandom(pubKey, new SecureRandom());
		engine.init(true, pwr);
		return engine.processBlock(srcData, 0, srcData.length);
	}

	/**
	 * @param priKey
	 * @param encryptedData
	 * @return
	 * @throws InvalidCipherTextException
	 */
	public static byte[] decrypt(ECPrivateKeyParameters priKey, byte[] encryptedData)
			throws InvalidCipherTextException {
		SM2Engine engine = new SM2Engine();
		engine.init(false, priKey);
		return engine.processBlock(encryptedData, 0, encryptedData.length);
	}

	public static ECPublicKeyParameters createECPublicKeyParameters(String xHex, String yHex, ECCurve curve,
			ECDomainParameters domainParameters) {
		byte[] xBytes = ByteUtils.fromHexString(xHex);
		byte[] yBytes = ByteUtils.fromHexString(yHex);
		return createECPublicKeyParameters(xBytes, yBytes, curve, domainParameters);
	}

	public static ECPublicKeyParameters createECPublicKeyParameters(byte[] xBytes, byte[] yBytes, ECCurve curve,
			ECDomainParameters domainParameters) {
		final byte uncompressedFlag = 0x04;
		byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length];
		encodedPubKey[0] = uncompressedFlag;
		System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length);
		System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length);
		return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters);
	}

	/**
	 * ECC公钥验签 不指定withId,则默认withId为字节数组:"1234567812345678".getBytes()
	 *
	 * @param pubKeyParameters
	 *            ECC公钥
	 * @param srcData
	 *            源数据
	 * @param sign
	 *            签名
	 * @return 验签成功返回true,失败返回false
	 */
	public static boolean verify(ECPublicKeyParameters pubKeyParameters, byte[] srcData, byte[] sign) {
		return verify(pubKeyParameters, null, srcData, sign);
	}

	/**
	 * ECC公钥验签
	 *
	 * @param pubKeyParameters
	 *            ECC公钥
	 * @param withId
	 *            可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
	 * @param srcData
	 *            源数据
	 * @param sign
	 *            签名
	 * @return 验签成功返回true,失败返回false
	 */
	public static boolean verify(ECPublicKeyParameters pubKeyParameters, byte[] withId, byte[] srcData, byte[] sign) {
		SM2Signer signer = new SM2Signer();
		CipherParameters param;
		if (withId != null) {
			param = new ParametersWithID(pubKeyParameters, withId);
		} else {
			param = pubKeyParameters;
		}
		signer.init(false, param);
		signer.update(srcData, 0, srcData.length);
		return signer.verifySignature(sign);
	}
}
2、测试类
import java.math.BigInteger;
import java.util.Arrays;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * @author alan 2018年12月11日
 */
public class SM2CoderTest {
	public static final byte[] WITH_ID = new byte[] { 1, 2, 3, 4 };
	public static final byte[] SRC_DATA = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

	@Test
	public void testEncryptAndDecrypt() {
		try {
			AsymmetricCipherKeyPair keyPair = SM2Coder.generateKeyPair();
			ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate();
			ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic();

			System.out.println("Pri Hex:" + ByteUtils.toHexString(priKey.getD().toByteArray()).toUpperCase());
			System.out.println(
					"Pub X Hex:" + ByteUtils.toHexString(pubKey.getQ().getAffineXCoord().getEncoded()).toUpperCase());
			System.out.println(
					"Pub X Hex:" + ByteUtils.toHexString(pubKey.getQ().getAffineYCoord().getEncoded()).toUpperCase());
			System.out.println("Pub Point Hex:" + ByteUtils.toHexString(pubKey.getQ().getEncoded(false)).toUpperCase());

			byte[] encryptedData = SM2Coder.encrypt(pubKey, SRC_DATA);
			System.out.println("SM2 encrypt result:\n" + ByteUtils.toHexString(encryptedData));
			byte[] decryptedData = SM2Coder.decrypt(priKey, encryptedData);
			System.out.println("SM2 decrypt result:\n" + ByteUtils.toHexString(decryptedData));
			if (!Arrays.equals(decryptedData, SRC_DATA)) {
				Assert.fail();
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			Assert.fail();
		}
	}

	@Test
	public void testSM2KeyRecovery() {
		try {
			String priHex = "5DD701828C424B84C5D56770ECF7C4FE882E654CAC53C7CC89A66B1709068B9D";
			String xHex = "FF6712D3A7FC0D1B9E01FF471A87EA87525E47C7775039D19304E554DEFE0913";
			String yHex = "F632025F692776D4C13470ECA36AC85D560E794E1BCCF53D82C015988E0EB956";
			String encodedPubHex = "04FF6712D3A7FC0D1B9E01FF471A87EA87525E47C7775039D19304E554DEFE0913F632025F692776D4C13470ECA36AC85D560E794E1BCCF53D82C015988E0EB956";
			String signHex = "30450220213C6CD6EBD6A4D5C2D0AB38E29D441836D1457A8118D34864C247D727831962022100D9248480342AC8513CCDF0F89A2250DC8F6EB4F2471E144E9A812E0AF497F801";
			byte[] signBytes = ByteUtils.fromHexString(signHex);
			byte[] src = ByteUtils.fromHexString("0102030405060708010203040506070801020304050607080102030405060708");
			byte[] withId = ByteUtils.fromHexString("31323334353637383132333435363738");

			ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(new BigInteger(ByteUtils.fromHexString(priHex)),
					SM2Coder.DOMAIN_PARAMS);
			ECPublicKeyParameters pubKey = SM2Coder.createECPublicKeyParameters(xHex, yHex, SM2Coder.CURVE,
					SM2Coder.DOMAIN_PARAMS);

			if (!SM2Coder.verify(pubKey, src, signBytes)) {
				Assert.fail("verify failed");
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			Assert.fail();
		}
	}
}

4、报文摘要

报文摘要与MD类似,只是算法不同。国密算法中SM3用作报文摘要。同样,由于报文摘要是不可逆的,所以只能是比对数字指纹。本文实现了两种,即hash和hmac。

1)、hash

1、构建字符串的hash
public static byte[] hash(byte[] srcData) {
	SM3Digest digest = new SM3Digest();
	digest.update(srcData, 0, srcData.length);
	byte[] hash = new byte[digest.getDigestSize()];
	digest.doFinal(hash, 0);
	return hash;
}
2、使用示例
byte[] hash = SM3Util.hash(SRC_DATA);
System.out.println("SM3 hash result:\n" + ByteUtils.toHexString(hash));
boolean flag = SM3Util.verify(SRC_DATA, hash);
if (!flag) {
	Assert.fail();
}

2)、hmac

1、增加了一个加密的密钥
public static byte[] hmac(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[] result = new byte[mac.getMacSize()];
	mac.doFinal(result, 0);
	return result;
}
2、使用示例
byte[] hmacKey = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
			byte[] hmac = SM3Util.hmac(hmacKey, SRC_DATA);
			System.out.println("SM3 hashmac result:\n" + Arrays.toString(hmac));

3)、完整示例

1、实现源码
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import java.util.Arrays;
import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * @author alan 2018年12月11日
 */
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;

import com.win.security.sm.BaseUtil;

import java.util.Arrays;

/**
 * @author alan 2018年12月11日
 */
public class SM3Util extends BaseUtil {
	/**
	 * 
	 * @param srcData
	 * @return
	 */
	public static byte[] hash(byte[] srcData) {
		SM3Digest digest = new SM3Digest();
		digest.update(srcData, 0, srcData.length);
		byte[] hash = new byte[digest.getDigestSize()];
		digest.doFinal(hash, 0);
		return hash;
	}

	/**
	 * 
	 * @param key
	 * @param srcData
	 * @return
	 */
	public static byte[] hmac(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[] result = new byte[mac.getMacSize()];
		mac.doFinal(result, 0);
		return result;
	}

	/**
	 * 
	 * @param srcData
	 * @param sm3Hash
	 * @return
	 */
	public static boolean verify(byte[] srcData, byte[] sm3Hash) {
		byte[] newHash = hash(srcData);
		if (Arrays.equals(newHash, sm3Hash)) {
			return true;
		} else {
			return false;
		}
	}
}
2、测试类
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import java.util.Arrays;
import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * @author alan 2018年12月11日
 */
public class SM3UtilTest {
	public static final byte[] SRC_DATA = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
	public static final byte[] WITH_ID = new byte[] { 1, 2, 3, 4 };

	@Test
	public void testHashAndVerify() {
		try {
			byte[] hash = SM3Util.hash(SRC_DATA);
			System.out.println("SM3 hash result:\n" + ByteUtils.toHexString(hash));
			boolean flag = SM3Util.verify(SRC_DATA, hash);
			if (!flag) {
				Assert.fail();
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			Assert.fail();
		}
	}

	@Test
	public void testHmacSM3() {
		try {
			byte[] hmacKey = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
			byte[] hmac = SM3Util.hmac(hmacKey, SRC_DATA);
			System.out.println("SM3 hashmac result:\n" + Arrays.toString(hmac));
		} catch (Exception ex) {
			ex.printStackTrace();
			Assert.fail();
		}
	}
}

以上,简单的介绍了国密的内容和对称加密、非对称加密和报文摘要的使用示例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一瓢一瓢的饮 alanchanchn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值