SM Java实现

【摘要】

本文主要讲解“国密加密算法”SM系列的Java实现方法,不涉及具体的算法剖析,在网络上找到的java实现方法比较少,切在跨语言加密解密上会存在一些问题,所以整理此文志之。

源码下载地址http://download.csdn.net/detail/ererfei/9474502 需要C#实现SM系列算法源码的可以评论留邮箱地址,看到后发送

1.SM2 & SM3

由于SM2算法中需要使用SM3摘要算法,所以把他们放在一起

项目目录结构如下:


首先要下载一个jar包——bcprov-jdk.jar,可以到maven库中下载最新版http://central.maven.org/maven2/org/bouncycastle/并将该jar包引入项目的classpath。实现代码如下(每个工具类都有Main可以运行测试):

a.      SM2主类

【SM2.java】

 

package com.mlq.sm;

import java.math.BigInteger;
import java.security.SecureRandom;

import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.ECFieldElement.Fp;

public class SM2 
{
	//测试参数
//	public static final String[] ecc_param = {
//	    "8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", 
//	    "787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 
//	    "63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 
//	    "8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 
//	    "421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 
//	    "0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2" 
//	};
	
	//正式参数
	public static String[] ecc_param = { 
		"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
		"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
		"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
		"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
		"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
		"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
	};

	public static SM2 Instance() 
	{
		return new SM2();
	}

	public final BigInteger ecc_p;
	public final BigInteger ecc_a;
	public final BigInteger ecc_b;
	public final BigInteger ecc_n;
	public final BigInteger ecc_gx;
	public final BigInteger ecc_gy;
	public final ECCurve ecc_curve;
	public final ECPoint ecc_point_g;
	public final ECDomainParameters ecc_bc_spec;
	public final ECKeyPairGenerator ecc_key_pair_generator;
	public final ECFieldElement ecc_gx_fieldelement;
	public final ECFieldElement ecc_gy_fieldelement;

	public SM2() 
	{
		this.ecc_p = new BigInteger(ecc_param[0], 16);
		this.ecc_a = new BigInteger(ecc_param[1], 16);
		this.ecc_b = new BigInteger(ecc_param[2], 16);
		this.ecc_n = new BigInteger(ecc_param[3], 16);
		this.ecc_gx = new BigInteger(ecc_param[4], 16);
		this.ecc_gy = new BigInteger(ecc_param[5], 16);

		this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);
		this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);

		this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
		this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);

		this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);

		ECKeyGenerationParameters ecc_ecgenparam;
		ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());

		this.ecc_key_pair_generator = new ECKeyPairGenerator();
		this.ecc_key_pair_generator.init(ecc_ecgenparam);
	}
}

b.      SM2工具类

【SM2Utils.java】

 

package com.mlq.sm;

import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

public class SM2Utils 
{
	//生成随机秘钥对
	public static void generateKeyPair(){
		SM2 sm2 = SM2.Instance();
		AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
		ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
		ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
		BigInteger privateKey = ecpriv.getD();
		ECPoint publicKey = ecpub.getQ();
		
		System.out.println("公钥: " + Util.byteToHex(publicKey.getEncoded()));
		System.out.println("私钥: " + Util.byteToHex(privateKey.toByteArray()));
	}
	
	//数据加密
	public static String encrypt(byte[] publicKey, byte[] data) throws IOException
	{
		if (publicKey == null || publicKey.length == 0)
		{
			return null;
		}
		
		if (data == null || data.length == 0)
		{
			return null;
		}
		
		byte[] source = new byte[data.length];
		System.arraycopy(data, 0, source, 0, data.length);
		
		Cipher cipher = new Cipher();
		SM2 sm2 = SM2.Instance();
		ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);
		
		ECPoint c1 = cipher.Init_enc(sm2, userKey);
		cipher.Encrypt(source);
		byte[] c3 = new byte[32];
		cipher.Dofinal(c3);
		
//		System.out.println("C1 " + Util.byteToHex(c1.getEncoded()));
//		System.out.println("C2 " + Util.byteToHex(source));
//		System.out.println("C3 " + Util.byteToHex(c3));
		//C1 C2 C3拼装成加密字串
		return Util.byteToHex(c1.getEncoded()) + Util.byteToHex(source) + Util.byteToHex(c3);
		
	}
	
	//数据解密
	public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException
	{
		if (privateKey == null || privateKey.length == 0)
		{
			return null;
		}
		
		if (encryptedData == null || encryptedData.length == 0)
		{
			return null;
		}
		//加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
		String data = Util.byteToHex(encryptedData);
		/***分解加密字串
		 * (C1 = C1标志位2位 + C1实体部分128位 = 130)
		 * (C3 = C3实体部分64位  = 64)
		 * (C2 = encryptedData.length * 2 - C1长度  - C2长度)
		 */
		byte[] c1Bytes = Util.hexToByte(data.substring(0,130));
		int c2Len = encryptedData.length - 97;
		byte[] c2 = Util.hexToByte(data.substring(130,130 + 2 * c2Len));
		byte[] c3 = Util.hexToByte(data.substring(130 + 2 * c2Len,194 + 2 * c2Len));
		
		SM2 sm2 = SM2.Instance();
		BigInteger userD = new BigInteger(1, privateKey);
		
		//通过C1实体字节来生成ECPoint
		ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
		Cipher cipher = new Cipher();
		cipher.Init_dec(userD, c1);
		cipher.Decrypt(c2);
		cipher.Dofinal(c3);
		
		//返回解密结果
		return c2;
	}
	
	public static void main(String[] args) throws Exception 
	{
		//生成密钥对
		generateKeyPair();
		
		String plainText = "ererfeiisgod";
		byte[] sourceData = plainText.getBytes();
		
		//下面的秘钥可以使用generateKeyPair()生成的秘钥内容
		// 国密规范正式私钥
		String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
		// 国密规范正式公钥
		String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";
		
		System.out.println("加密: ");
		String cipherText = SM2Utils.encrypt(Util.hexToByte(pubk), sourceData);
		System.out.println(cipherText);
		System.out.println("解密: ");
		plainText = new String(SM2Utils.decrypt(Util.hexToByte(prik), Util.hexToByte(cipherText)));
		System.out.println(plainText);
		
	}
}

c.      SM3主类

【SM3.java】

 

package com.mlq.sm;

public class SM3 
{
	public static final byte[] iv = { 0x73, (byte) 0x80, 0x16, 0x6f, 0x49,
		0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42, (byte) 0xd7,
		(byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30,
		(byte) 0xbc, (byte) 0x16, 0x31, 0x38, (byte) 0xaa, (byte) 0xe3,
		(byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e,
		0x4e };
	
	public static int[] Tj = new int[64];
	
	static 
	{
		for (int i = 0; i < 16; i++) 
		{
			Tj[i] = 0x79cc4519;
		}
	
		for (int i = 16; i < 64; i++) 
		{
			Tj[i] = 0x7a879d8a;
		}
	}

	public static byte[] CF(byte[] V, byte[] B) 
	{
		int[] v, b;
		v = convert(V);
		b = convert(B);
		return convert(CF(v, b));
	}

	private static int[] convert(byte[] arr)
	{
		int[] out = new int[arr.length / 4];
		byte[] tmp = new byte[4];
		for (int i = 0; i < arr.length; i += 4) 
		{
			System.arraycopy(arr, i, tmp, 0, 4);
			out[i / 4] = bigEndianByteToInt(tmp);
		}
		return out;
	}

	private static byte[] convert(int[] arr) 
	{
		byte[] out = new byte[arr.length * 4];
		byte[] tmp = null;
		for (int i = 0; i < arr.length; i++) 
		{
			tmp = bigEndianIntToByte(arr[i]);
			System.arraycopy(tmp, 0, out, i * 4, 4);
		}
		return out;
	}

	public static int[] CF(int[] V, int[] B) 
	{
		int a, b, c, d, e, f, g, h;
		int ss1, ss2, tt1, tt2;
		a = V[0];
		b = V[1];
		c = V[2];
		d = V[3];
		e = V[4];
		f = V[5];
		g = V[6];
		h = V[7];
		
		int[][] arr = expand(B);
		int[] w = arr[0];
		int[] w1 = arr[1];
		
		for (int j = 0; j < 64; j++)
		{
			ss1 = (bitCycleLeft(a, 12) + e + bitCycleLeft(Tj[j], j));
			ss1 = bitCycleLeft(ss1, 7);
			ss2 = ss1 ^ bitCycleLeft(a, 12);
			tt1 = FFj(a, b, c, j) + d + ss2 + w1[j];
			tt2 = GGj(e, f, g, j) + h + ss1 + w[j];
			d = c;
			c = bitCycleLeft(b, 9);
			b = a;
			a = tt1;
			h = g;
			g = bitCycleLeft(f, 19);
			f = e;
			e = P0(tt2);

			/*System.out.print(j+" ");
			System.out.print(Integer.toHexString(a)+" ");
			System.out.print(Integer.toHexString(b)+" ");
			System.out.print(Integer.toHexString(c)+" ");
			System.out.print(Integer.toHexString(d)+" ");
			System.out.print(Integer.toHexString(e)+" ");
			System.out.print(Integer.toHexString(f)+" ");
			System.out.print(Integer.toHexString(g)+" ");
			System.out.print(Integer.toHexString(h)+" ");
			System.out.println("");*/
		}
//		
  • 18
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 67
    评论
关于SM3算法的Java实现,可以使用Bouncy Castle密码库来实现。Bouncy Castle是一个流行的密码学库,提供了各种密码算法的实现,包括SM3算法。 以下是一个使用Bouncy Castle库实现SM3算法的Java代码示例: ```java import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.util.encoders.Hex; public class SM3Example { public static void main(String\[\] args) { // 输入消息 byte\[\] input = "Hello, SM3!".getBytes(); // 创建SM3Digest实例 SM3Digest digest = new SM3Digest(); // 更新消息 digest.update(input, 0, input.length); // 计算杂凑值 byte\[\] hash = new byte\[digest.getDigestSize()\]; digest.doFinal(hash, 0); // 将杂凑值转换为十六进制字符串 String hashHex = Hex.toHexString(hash); // 输出结果 System.out.println("SM3 Hash: " + hashHex); } } ``` 这段代码使用Bouncy Castle库中的`SM3Digest`类来计算SM3算法的杂凑值。首先,创建一个`SM3Digest`实例,然后使用`update`方法更新输入消息,最后使用`doFinal`方法计算杂凑值。最终,将杂凑值转换为十六进制字符串并输出。 请注意,使用Bouncy Castle库需要先下载并导入相应的库文件。你可以在Bouncy Castle官方网站上找到相关的下载和导入指南。 希望这个示例能帮助到你实现SM3算法的Java代码。 #### 引用[.reference_title] - *1* *2* *3* [SM系列国密算法](https://blog.csdn.net/qq_27731689/article/details/113615774)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 67
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱清清

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

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

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

打赏作者

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

抵扣说明:

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

余额充值