SM2國密算法

目錄java

爲什麼由RSA轉爲SM2git

1,安全算法

2,快速數組

SM算法介紹安全

1,總體介紹服務器

2,加密過程app

SM算法Demo實現(Java)dom


爲什麼由RSA轉爲SM2

1,安全

RSA算法---基於大整數因子分解數學難題(IFP)設計的,其數學原理相對簡單,易於實現,單位安全強度相對較低。對大整數作因子分解的難度決定了RSA算法的可靠性,隨着計算機運算速度的提升和分佈式計算的發展,加上因子分解方法的改進,對低位數的密鑰破解已成爲可能。分佈式

ECC---橢圓曲線密碼編碼學,基於橢圓曲線上離散對數計算難題(ECDLP)。數學理論複雜,安全性高。用國際公認針對ECC算法最有效攻擊方法—Pollard rho方法破譯,難度基本上是指數級。函數

基於ECC的SM2證書廣泛採用256位密鑰長度,加密強度等同於3072位RSA證書。遠高於業界廣泛採用的2048位RSA證書。

2,快速

ECC算法可以以較小的密鑰和較少的數據傳遞創建HTTPS鏈接,相同安全強度下鏈接速度更快,服務器響應更快。

SM算法介紹

1,總體介紹

       SM1 爲對稱加密。加密強度與AES至關。算法不公開,調用該算法時,須要經過加密芯片的接口進行調用。

       SM2非對稱加密算法是我國基於ECC橢圓曲線密碼理論自主研發設計,由國家密碼管理局於2010年12月17日發佈,包括SM2-1橢圓曲線數字簽名算法,SM2-2橢圓曲線密鑰交換協議,SM2-3橢圓曲線公鑰加密算法,分別用於實現數字簽名密鑰協商和數據加密等功能。在密碼行業標準GMT 0003.1-2012 SM2 總則中推薦了一條256位曲線做爲標準曲線。數字簽名算法、密鑰交換協議以及公鑰加密算法都根據SM2總則選取的有限域和橢圓曲線生成密鑰對;在數字簽名、密鑰交換方與ECDSA、ECDH等國際算法不一樣,升級了安全的機制、計算量和複雜性;在數字簽名和驗證、消息認證碼的生成與驗證以及隨機數的生成等方面,使用國密局批准的SM3密碼雜湊算法和隨機數生成器。SM2有公鑰、私鑰之分,公鑰給別人,能夠在必定範圍內公開,私鑰留給本身,必須保密。由私鑰能夠計算公鑰;由公鑰現階段沒法計算私鑰;

      SM3雜湊算法是我國自主設計的密碼雜湊算法,適用於商用密碼應用中的數字簽名和驗證消息認證碼的生成與驗證以及隨機數的生成,可知足多種密碼應用的安全需求。安全性要高於MD5算法(128位)和SHA-1算法(160位),SM3算法的壓縮函數與SHA-256具備類似結構,但設計更加複雜;例如MD5輸出128比特雜湊值,SHA-1輸出160比特,SM3算法256比特;

     SM4是我國自主設計的分組對稱密碼算法,要保證一個對稱密碼算法的安全性的基本條件是其具有足夠的密鑰長度。SM4與AES算法具備相同的密鑰長度128位,在安全性上高於3DES算法,在實際應用中可以抵抗針對分組密碼算法的各類攻擊方法。

2,加密過程

私鑰dA和公鑰PA=[dA]G= (xA,yA),SM2算法想要成功解密,必須使用與加密公鑰對於的私鑰,這樣才能經過密鑰派生函數計算出的異或比特串才能和加密時計算的異或比特串徹底一致

過程

設須要發送的消息爲比特串 M ,klen 爲 M 的比特長度。

爲了對明文 M 進行加密,做爲加密者的用戶 A 應實現如下運算步驟:

A1:用隨機數發生器產生隨機數k∈[1,n-1];

A2:計算橢圓曲線點 C1=[k]G=(x1,y1),([k]G 表示 k*G )將C1的數據類型轉換爲比特串;

A3:計算橢圓曲線點 S=[h]PB,若S是無窮遠點,則報錯並退出;

A4:計算橢圓曲線點 [k]PB=(x2,y2),將座標 x二、y2 的數據類型轉換爲比特串;

A5:計算t=KDF(x2 ∥ y2, klen),若 t 爲全0比特串,則返回 A1;

A6:計算C2 = M ⊕ t;

A7:計算C3 = Hash(x2 ∥ M ∥ y2);

A8:輸出密文C = C1 ∥ C2 ∥ C3。

解密過程:

設klen爲密文中C2的比特長度。

爲了對密文C=C1 ∥ C2 ∥ C3 進行解密,做爲解密者的用戶 B 應實現如下運算步驟:

B1:從C中取出比特串C1,將C1的數據類型轉換爲橢圓曲線上的點,驗證C1是否知足橢圓曲線方程,若不知足則報錯並退出;

B2:計算橢圓曲線點 S=[h]C1,若S是無窮遠點,則報錯並退出;

B3:計算[dB]C1=(x2,y2),將座標x二、y2的數據類型轉換爲比特串;

B4:計算t=KDF(x2 ∥ y2, klen),若t爲全0比特串,則報錯並退出;

B5:從C中取出比特串C2,計算M′ = C2 ⊕ t;

B6:計算u = Hash(x2 ∥ M′ ∥ y2),從C中取出比特串C3,若u != C3,則報錯並退出;

B7:輸出明文M′。

原理:

用戶 A 持有公鑰PB=[dB]G(僅有PB值),用戶 B 持有私鑰 dB

加密:C1=k*G C2=M⊕(k*PB) 解密:M′=C2 ⊕ (dB*C1) # 這裏只敘述基本原理,便於理解

證實:dB*C1=dB*k*G=k*(dB*G)=k*PB 所以,M′=C2 ⊕ (dB*C1)=M⊕(k*PB)⊕(k*PB)=M 得證

注:此實現算法所研究的橢圓曲線是基於域 Fp 上的橢圓曲線

安全參數設置:

隨機數 k 和私鑰 dB 最好大點,2*50 以上比較安全

SM算法Demo實現(Java)

SM2KeyPair 祕鑰對類

package org.pzone.crypto;

import java.math.BigInteger;
import org.bouncycastle.math.ec.ECPoint;
/**
 * SM2密鑰對Bean
 */
public class SM2KeyPair {

	private final ECPoint publicKey;
	private final BigInteger privateKey;

	SM2KeyPair(ECPoint publicKey, BigInteger privateKey) {
		this.publicKey = publicKey;
		this.privateKey = privateKey;
	}
	public ECPoint getPublicKey() {
		return publicKey;
	}
	public BigInteger getPrivateKey() {
		return privateKey;
	}

}

SM2類

package org.pzone.crypto;

import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;

import java.io.*;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;

/**
 * SM2公鑰加密算法實現 包括 -簽名,驗籤 -密鑰交換 -公鑰加密,私鑰解密
 * 
 * @author 
 *
 */
public class SM2 {
	//SM2橢 橢 圓 曲 線 公 鑰 密 碼 算 法 推 薦 曲 線 參 數
	//推薦使用素數域256位橢圓曲線。
	//橢圓曲線方程:y 2 = x 3 + ax + b。
	private static BigInteger n = new BigInteger(
			"FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16);
	private static BigInteger p = new BigInteger(
			"FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16);
	private static BigInteger a = new BigInteger(
			"FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16);
	private static BigInteger b = new BigInteger(
			"28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16);
	private static BigInteger gx = new BigInteger(
			"32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16);
	private static BigInteger gy = new BigInteger(
			"BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16);
	private static ECDomainParameters ecc_bc_spec;
	private static int w = (int) Math.ceil(n.bitLength() * 1.0 / 2) - 1;
	private static BigInteger _2w = new BigInteger("2").pow(w);
	private static final int DIGEST_LENGTH = 32;

	private static SecureRandom random = new SecureRandom();
	private static ECCurve.Fp curve;
	private static ECPoint G;
	private boolean debug = false;

	public boolean isDebug() {
		return debug;
	}

	public void setDebug(boolean debug) {
		this.debug = debug;
	}

	/**
	 * 以16進制打印字節數組
	 * 
	 * @param b
	 */
	public static void printHexString(byte[] b) {
		for (int i = 0; i < b.length; i++) {
			String hex = Integer.toHexString(b[i] & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			System.out.print(hex.toUpperCase());
		}
		System.out.println();
	}

	/**
	 * 隨機數生成器
	 * 
	 * @param max
	 * @return
	 */
	private static BigInteger random(BigInteger max) {
		BigInteger r = new BigInteger(256, random);
		while (r.compareTo(max) >= 0) {
			r = new BigInteger(128, random);
		}
		return r;
	}

	/**
	 * 判斷字節數組是否全0
	 * 
	 * @param buffer
	 * @return
	 */
	private boolean allZero(byte[] buffer) {
		for (int i = 0; i < buffer.length; i++) {
			if (buffer[i] != 0)
				return false;
		}
		return true;
	}

	/**
	 * 公鑰加密
	 * 
	 * @param input
	 *            加密原文
	 * @param publicKey
	 *            公鑰
	 * @return
	 */
	public byte[] encrypt(String input, ECPoint publicKey) {

		byte[] inputBuffer = input.getBytes();
		if (debug)
			printHexString(inputBuffer);

		byte[] C1Buffer;
		ECPoint kpb;
		byte[] t;
		do {
			/* 1 產生隨機數k,k屬於[1, n-1] */
			BigInteger k = random(n);
			if (debug) {
				System.out.print("k: ");
				printHexString(k.toByteArray());
			}

			/* 2 計算橢圓曲線點C1 = [k]G = (x1, y1) */
			ECPoint C1 = G.multiply(k);
			C1Buffer = C1.getEncoded(false);
			if (debug) {
				System.out.print("C1: ");
				printHexString(C1Buffer);
			}

			/*
			 * 3 計算橢圓曲線點 S = [h]Pb
			 */
			BigInteger h = ecc_bc_spec.getH();
			if (h != null) {
				ECPoint S = publicKey.multiply(h);
				if (S.isInfinity())
					throw new IllegalStateException();
			}

			/* 4 計算 [k]PB = (x2, y2) */
			kpb = publicKey.multiply(k).normalize();

			/* 5 計算 t = KDF(x2||y2, klen) */
			byte[] kpbBytes = kpb.getEncoded(false);
			t = KDF(kpbBytes, inputBuffer.length);
			// DerivationFunction kdf = new KDF1BytesGenerator(new
			// ShortenedDigest(new SHA256Digest(), DIGEST_LENGTH));
			//
			// t = new byte[inputBuffer.length];
			// kdf.init(new ISO18033KDFParameters(kpbBytes));
			// kdf.generateBytes(t, 0, t.length);
		} while (allZero(t));

		/* 6 計算C2=M^t */
		byte[] C2 = new byte[inputBuffer.length];
		for (int i = 0; i < inputBuffer.length; i++) {
			C2[i] = (byte) (inputBuffer[i] ^ t[i]);
		}

		/* 7 計算C3 = Hash(x2 || M || y2) */
		byte[] C3 = sm3hash(kpb.getXCoord().toBigInteger().toByteArray(), inputBuffer,
				kpb.getYCoord().toBigInteger().toByteArray());

		/* 8 輸出密文 C=C1 || C2 || C3 */

		byte[] encryptResult = new byte[C1Buffer.length + C2.length + C3.length];

		System.arraycopy(C1Buffer, 0, encryptResult, 0, C1Buffer.length);
		System.arraycopy(C2, 0, encryptResult, C1Buffer.length, C2.length);
		System.arraycopy(C3, 0, encryptResult, C1Buffer.length + C2.length, C3.length);

		if (debug) {
			System.out.print("密文: ");
			printHexString(encryptResult);
		}

		return encryptResult;
	}

	/**
	 * 私鑰解密
	 * 
	 * @param encryptData
	 *            密文數據字節數組
	 * @param privateKey
	 *            解密私鑰
	 * @return
	 */
	public String decrypt(byte[] encryptData, BigInteger privateKey) {
		System.out.println("111");

		if (debug)
			System.out.println("encryptData length: " + encryptData.length);

		byte[] C1Byte = new byte[65];
		System.arraycopy(encryptData, 0, C1Byte, 0, C1Byte.length);

		ECPoint C1 = curve.decodePoint(C1Byte).normalize();

		/*
		 * 計算橢圓曲線點 S = [h]C1 是否爲無窮點
		 */
		BigInteger h = ecc_bc_spec.getH();
		if (h != null) {
			ECPoint S = C1.multiply(h);
			if (S.isInfinity())
				throw new IllegalStateException();
		}
		/* 計算[dB]C1 = (x2, y2) */
		ECPoint dBC1 = C1.multiply(privateKey).normalize();

		/* 計算t = KDF(x2 || y2, klen) */
		byte[] dBC1Bytes = dBC1.getEncoded(false);
		int klen = encryptData.length - 65 - DIGEST_LENGTH;
		byte[] t = KDF(dBC1Bytes, klen);

		if (allZero(t)) {
			System.err.println("all zero");
			throw new IllegalStateException();
		}

		/* 5 計算M'=C2^t */
		byte[] M = new byte[klen];
		for (int i = 0; i < M.length; i++) {
			M[i] = (byte) (encryptData[C1Byte.length + i] ^ t[i]);
		}
		if (debug)
			printHexString(M);

		/* 6 計算 u = Hash(x2 || M' || y2) 判斷 u == C3是否成立 */
		byte[] C3 = new byte[DIGEST_LENGTH];

		if (debug)
			try {
				System.out.println("M = " + new String(M, "UTF8"));
			} catch (UnsupportedEncodingException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

		System.arraycopy(encryptData, encryptData.length - DIGEST_LENGTH, C3, 0, DIGEST_LENGTH);
		byte[] u = sm3hash(dBC1.getXCoord().toBigInteger().toByteArray(), M,
				dBC1.getYCoord().toBigInteger().toByteArray());
		if (Arrays.equals(u, C3)) {
			if (debug)
				System.out.println("解密成功");
			try {
				return new String(M, "UTF8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			return null;
		} else {
			if (debug) {
				System.out.print("u = ");
				printHexString(u);
				System.out.print("C3 = ");
				printHexString(C3);
				System.err.println("解密驗證失敗");
			}
			return null;
		}

	}

	/**
	 * 判斷是否在範圍內
	 * 
	 * @param param
	 * @param min
	 * @param max
	 * @return
	 */
	private boolean between(BigInteger param, BigInteger min, BigInteger max) {
		if (param.compareTo(min) >= 0 && param.compareTo(max) < 0) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 判斷生成的公鑰是否合法
	 * 
	 * @param publicKey
	 * @return
	 */
	private boolean checkPublicKey(ECPoint publicKey) {

		if (!publicKey.isInfinity()) {

			BigInteger x = publicKey.getXCoord().toBigInteger();
			BigInteger y = publicKey.getYCoord().toBigInteger();

			if (between(x, new BigInteger("0"), p) && between(y, new BigInteger("0"), p)) {
				BigInteger xResult = x.pow(3).add(a.multiply(x)).add(b).mod(p);
				if (debug)
					System.out.println("xResult: " + xResult.toString());
				BigInteger yResult = y.pow(2).mod(p);
				if (debug)
					System.out.println("yResult: " + yResult.toString());
				if (yResult.equals(xResult) && publicKey.multiply(n).isInfinity()) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 生成密鑰對
	 * 
	 * @return
	 */
	public SM2KeyPair generateKeyPair() {

		BigInteger d = random(n.subtract(new BigInteger("1")));

		SM2KeyPair keyPair = new SM2KeyPair(G.multiply(d).normalize(), d);

		if (checkPublicKey(keyPair.getPublicKey())) {
			if (debug)
				System.out.println("generate key successfully");
			return keyPair;
		} else {
			if (debug)
				System.err.println("generate key failed");
			return null;
		}
	}

	public SM2() {
		curve = new ECCurve.Fp(
				p, // q
				a, // a
				b); // b
		G = curve.createPoint(gx, gy);
		ecc_bc_spec = new ECDomainParameters(curve, G, n);
	}

	public SM2(boolean debug) {
		this();
		this.debug = debug;
	}

	/**
	 * 導出公鑰到本地
	 * 
	 * @param publicKey
	 * @param path
	 */
	public void exportPublicKey(ECPoint publicKey, String path) {
		File file = new File(path);
		try  {
			if (!file.exists())
				file.createNewFile();
			byte buffer[] = publicKey.getEncoded(false);
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(buffer);
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 從本地導入公鑰
	 * 
	 * @param path
	 * @return
	 */
	public ECPoint 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();
			return curve.decodePoint(baos.toByteArray());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 導出私鑰到本地
	 * 
	 * @param privateKey
	 * @param path
	 */
	public void exportPrivateKey(BigInteger privateKey, String path) {
		File file = new File(path);
		try {
			if (!file.exists())
				file.createNewFile();
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
			oos.writeObject(privateKey);
			oos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 從本地導入私鑰
	 * 
	 * @param path
	 * @return
	 */
	public BigInteger importPrivateKey(String path) {
		File file = new File(path);
		try {
			if (!file.exists())
				return null;
			FileInputStream fis = new FileInputStream(file);
			ObjectInputStream ois = new ObjectInputStream(fis);
			BigInteger res = (BigInteger) (ois.readObject());
			ois.close();
			fis.close();
			return res;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 字節數組拼接
	 * 
	 * @param params
	 * @return
	 */
	private static byte[] join(byte[]... params) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] res = null;
		try {
			for (int i = 0; i < params.length; i++) {
				baos.write(params[i]);
			}
			res = baos.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return res;
	}

	/**
	 * sm3摘要
	 * 
	 * @param params
	 * @return
	 */
	private static byte[] sm3hash(byte[]... params) {
		byte[] res = null;
		try {
			res = SM3.hash(join(params));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return res;
	}

	/**
	 * 取得用戶標識字節數組
	 * 
	 * @param IDA
	 * @param aPublicKey
	 * @return
	 */
	private static byte[] ZA(String IDA, ECPoint aPublicKey) {
		byte[] idaBytes = IDA.getBytes();
		int entlenA = idaBytes.length * 8;
		byte[] ENTLA = new byte[] { (byte) (entlenA & 0xFF00), (byte) (entlenA & 0x00FF) };
		byte[] ZA = sm3hash(ENTLA, idaBytes, a.toByteArray(), b.toByteArray(), gx.toByteArray(), gy.toByteArray(),
				aPublicKey.getXCoord().toBigInteger().toByteArray(),
				aPublicKey.getYCoord().toBigInteger().toByteArray());
		return ZA;
	}

	/**
	 * 簽名
	 * 
	 * @param M
	 *            簽名信息
	 * @param IDA
	 *            簽名方惟一標識
	 * @param keyPair
	 *            簽名方密鑰對
	 * @return 簽名
	 */
	public Signature sign(String M, String IDA, SM2KeyPair keyPair) {
		byte[] ZA = ZA(IDA, keyPair.getPublicKey());
		byte[] M_ = join(ZA, M.getBytes());
		BigInteger e = new BigInteger(1, sm3hash(M_));
		// BigInteger k = new BigInteger(
		// "6CB28D99 385C175C 94F94E93 4817663F C176D925 DD72B727 260DBAAE
		// 1FB2F96F".replace(" ", ""), 16);
		BigInteger k;
		BigInteger r;
		do {
			k = random(n);
			ECPoint p1 = G.multiply(k).normalize();
			BigInteger x1 = p1.getXCoord().toBigInteger();
			r = e.add(x1);
			r = r.mod(n);
		} while (r.equals(BigInteger.ZERO) || r.add(k).equals(n));

		BigInteger s = ((keyPair.getPrivateKey().add(BigInteger.ONE).modInverse(n))
				.multiply((k.subtract(r.multiply(keyPair.getPrivateKey()))).mod(n))).mod(n);

		return new Signature(r, s);
	}

	/**
	 * 驗籤
	 * 
	 * @param M
	 *            簽名信息
	 * @param signature
	 *            簽名
	 * @param IDA
	 *            簽名方惟一標識
	 * @param aPublicKey
	 *            簽名方公鑰
	 * @return true or false
	 */
	public boolean verify(String M, Signature signature, String IDA, ECPoint aPublicKey) {
		if (!between(signature.r, BigInteger.ONE, n))
			return false;
		if (!between(signature.s, BigInteger.ONE, n))
			return false;

		byte[] M_ = join(ZA(IDA, aPublicKey), M.getBytes());
		BigInteger e = new BigInteger(1, sm3hash(M_));
		BigInteger t = signature.r.add(signature.s).mod(n);

		if (t.equals(BigInteger.ZERO))
			return false;

		ECPoint p1 = G.multiply(signature.s).normalize();
		ECPoint p2 = aPublicKey.multiply(t).normalize();
		BigInteger x1 = p1.add(p2).normalize().getXCoord().toBigInteger();
		BigInteger R = e.add(x1).mod(n);
		if (R.equals(signature.r))
			return true;
		return false;
	}

	/**
	 * 密鑰派生函數
	 * 
	 * @param Z
	 * @param klen
	 *            生成klen字節數長度的密鑰
	 * @return
	 */
	private static byte[] KDF(byte[] Z, int klen) {
		int ct = 1;
		int end = (int) Math.ceil(klen * 1.0 / 32);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			for (int i = 1; i < end; i++) {
				baos.write(sm3hash(Z, SM3.toByteArray(ct)));
				ct++;
			}
			byte[] last = sm3hash(Z, SM3.toByteArray(ct));
			if (klen % 32 == 0) {
				baos.write(last);
			} else
				baos.write(last, 0, klen % 32);
			return baos.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 傳輸實體類
	 * 
	 * @author Potato
	 *
	 */
	private static class TransportEntity implements Serializable {
		final byte[] R; //R點
		final byte[] S; //驗證S
		final byte[] Z; //用戶標識
		final byte[] K; //公鑰

		public TransportEntity(byte[] r, byte[] s,byte[] z,ECPoint pKey) {
			R = r;
			S = s;
			Z=z;
			K=pKey.getEncoded(false);
		}
	}

	/**
	 * 密鑰協商輔助類
	 * 
	 * @author Potato
	 *
	 */
	public static class KeyExchange {
		BigInteger rA;
		ECPoint RA;
		ECPoint V;
		byte[] Z;
		byte[] key;
		
		String ID;
		SM2KeyPair keyPair;

		public KeyExchange(String ID,SM2KeyPair keyPair) {
			this.ID=ID;
			this.keyPair = keyPair;
			this.Z=ZA(ID, keyPair.getPublicKey());
		}

		/**
		 * 密鑰協商發起第一步
		 * 
		 * @return
		 */
		public TransportEntity keyExchange_1() {
			rA = random(n);
			// rA=new BigInteger("83A2C9C8 B96E5AF7 0BD480B4 72409A9A 327257F1 EBB73F5B 073354B2 48668563".replace(" ", ""),16);
			RA = G.multiply(rA).normalize();
			return new TransportEntity(RA.getEncoded(false), null,Z,keyPair.getPublicKey());
		}

		/**
		 * 密鑰協商響應方
		 * 
		 * @param entity 傳輸實體
		 * @return
		 */
		public TransportEntity keyExchange_2(TransportEntity entity) {
			BigInteger rB = random(n);
			// BigInteger rB=new BigInteger("33FE2194 0342161C 55619C4A 0C060293 D543C80A F19748CE 176D8347 7DE71C80".replace(" ", ""),16);
			ECPoint RB = G.multiply(rB).normalize();
			
			this.rA=rB;
			this.RA=RB;

			BigInteger x2 = RB.getXCoord().toBigInteger();
			x2 = _2w.add(x2.and(_2w.subtract(BigInteger.ONE)));

			BigInteger tB = keyPair.getPrivateKey().add(x2.multiply(rB)).mod(n);
			ECPoint RA = curve.decodePoint(entity.R).normalize();
			
			BigInteger x1 = RA.getXCoord().toBigInteger();
			x1 = _2w.add(x1.and(_2w.subtract(BigInteger.ONE)));

			ECPoint aPublicKey=curve.decodePoint(entity.K).normalize();
			ECPoint temp = aPublicKey.add(RA.multiply(x1).normalize()).normalize();
			ECPoint V = temp.multiply(ecc_bc_spec.getH().multiply(tB)).normalize();
			if (V.isInfinity())
				throw new IllegalStateException();
			this.V=V;
			
			byte[] xV = V.getXCoord().toBigInteger().toByteArray();
			byte[] yV = V.getYCoord().toBigInteger().toByteArray();
			byte[] KB = KDF(join(xV, yV, entity.Z, this.Z), 16);
			key = KB;
			System.out.print("協商得B密鑰:");
			printHexString(KB);
			byte[] sB = sm3hash(new byte[] { 0x02 }, yV,
					sm3hash(xV, entity.Z, this.Z, RA.getXCoord().toBigInteger().toByteArray(),
							RA.getYCoord().toBigInteger().toByteArray(), RB.getXCoord().toBigInteger().toByteArray(),
							RB.getYCoord().toBigInteger().toByteArray()));
			return new TransportEntity(RB.getEncoded(false), sB,this.Z,keyPair.getPublicKey());
		}

		/**
		 * 密鑰協商發起方第二步
		 * 
		 * @param entity 傳輸實體
		 */
		public TransportEntity keyExchange_3(TransportEntity entity) {
			BigInteger x1 = RA.getXCoord().toBigInteger();
			x1 = _2w.add(x1.and(_2w.subtract(BigInteger.ONE)));

			BigInteger tA = keyPair.getPrivateKey().add(x1.multiply(rA)).mod(n);
			ECPoint RB = curve.decodePoint(entity.R).normalize();
			
			BigInteger x2 = RB.getXCoord().toBigInteger();
			x2 = _2w.add(x2.and(_2w.subtract(BigInteger.ONE)));

			ECPoint bPublicKey=curve.decodePoint(entity.K).normalize();
			ECPoint temp = bPublicKey.add(RB.multiply(x2).normalize()).normalize();
			ECPoint U = temp.multiply(ecc_bc_spec.getH().multiply(tA)).normalize();
			if (U.isInfinity())
				throw new IllegalStateException();
			this.V=U;
			
			byte[] xU = U.getXCoord().toBigInteger().toByteArray();
			byte[] yU = U.getYCoord().toBigInteger().toByteArray();
			byte[] KA = KDF(join(xU, yU,
					this.Z, entity.Z), 16);
			key = KA;
			System.out.print("協商得A密鑰:");
			printHexString(KA);
			byte[] s1= sm3hash(new byte[] { 0x02 }, yU,
					sm3hash(xU, this.Z, entity.Z, RA.getXCoord().toBigInteger().toByteArray(),
							RA.getYCoord().toBigInteger().toByteArray(), RB.getXCoord().toBigInteger().toByteArray(),
							RB.getYCoord().toBigInteger().toByteArray()));
			if(Arrays.equals(entity.S, s1))
				System.out.println("B->A 密鑰確認成功");
			else
				System.out.println("B->A 密鑰確認失敗");
			byte[] sA= sm3hash(new byte[] { 0x03 }, yU,
					sm3hash(xU, this.Z, entity.Z, RA.getXCoord().toBigInteger().toByteArray(),
							RA.getYCoord().toBigInteger().toByteArray(), RB.getXCoord().toBigInteger().toByteArray(),
							RB.getYCoord().toBigInteger().toByteArray()));
			
			return new TransportEntity(RA.getEncoded(false), sA,this.Z,keyPair.getPublicKey());
		}
		
		/**
		 * 密鑰確認最後一步
		 * 
		 * @param entity 傳輸實體
		 */
		public void keyExchange_4(TransportEntity entity) {
			byte[] xV = V.getXCoord().toBigInteger().toByteArray();
			byte[] yV = V.getYCoord().toBigInteger().toByteArray();
			ECPoint RA = curve.decodePoint(entity.R).normalize();
			byte[] s2= sm3hash(new byte[] { 0x03 }, yV,
					sm3hash(xV, entity.Z, this.Z, RA.getXCoord().toBigInteger().toByteArray(),
							RA.getYCoord().toBigInteger().toByteArray(), this.RA.getXCoord().toBigInteger().toByteArray(),
							this.RA.getYCoord().toBigInteger().toByteArray()));
			if(Arrays.equals(entity.S, s2))
				System.out.println("A->B 密鑰確認成功");
			else
				System.out.println("A->B 密鑰確認失敗");
		}
	}

	public static void main(String[] args) throws UnsupportedEncodingException {

		SM2 sm02 = new SM2();
//		 BigInteger px = new BigInteger(
//		 "0AE4C779 8AA0F119 471BEE11 825BE462 02BB79E2 A5844495 E97C04FF 4DF2548A".replace(" ", ""), 16);
//		 BigInteger py = new BigInteger(
//		 "7C0240F8 8F1CD4E1 6352A73C 17B7F16F 07353E53 A176D684 A9FE0C6B B798E857".replace(" ", ""), 16);
//		 ECPoint publicKey = sm02.curve.createPoint(px, py);
//		 BigInteger privateKey = new BigInteger(
//		 "128B2FA8 BD433C6C 068C8D80 3DFF7979 2A519A55 171B1B65 0C23661D 15897263".replace(" ", ""), 16);

//先生成pem文件。要不沒法加載
//		 SM2KeyPair keyPair = sm02.generateKeyPair();
//		 ECPoint publicKey=keyPair.getPublicKey();
//		 BigInteger privateKey=keyPair.getPrivateKey();
//		 sm02.exportPublicKey(publicKey, "D:/publickey.pem");
//		 sm02.exportPrivateKey(privateKey, "D:/privatekey.pem");

		System.out.println("-----------------公鑰加密與解密-----------------");
		ECPoint publicKey = sm02.importPublicKey("D:/publickey.pem");
		BigInteger privateKey = sm02.importPrivateKey("D:/privatekey.pem");
		byte[] data = sm02.encrypt("測試加密aaaaaaaaaaa123aabb", publicKey);
		System.out.print("密文:");
		SM2.printHexString(data);
		System.out.println("解密後明文:" + sm02.decrypt(data, privateKey));

		System.out.println("-----------------簽名與驗籤-----------------");
		String IDA = "Heartbeats";
		String M = "要簽名的信息";
		Signature signature = sm02.sign(M, IDA, new SM2KeyPair(publicKey, privateKey));
		System.out.println("用戶標識:" + IDA);
		System.out.println("簽名信息:" + M);
		System.out.println("數字簽名:" + signature);
		System.out.println("驗證簽名:" + sm02.verify(M, signature, IDA, publicKey));

		System.out.println("-----------------密鑰協商-----------------");
		String aID = "AAAAAAAAAAAAA";
		SM2KeyPair aKeyPair = sm02.generateKeyPair();
		KeyExchange aKeyExchange = new KeyExchange(aID,aKeyPair);

		String bID = "BBBBBBBBBBBBB";
		SM2KeyPair bKeyPair = sm02.generateKeyPair();
		KeyExchange bKeyExchange = new KeyExchange(bID,bKeyPair);
		TransportEntity entity1 = aKeyExchange.keyExchange_1();
		TransportEntity entity2 = bKeyExchange.keyExchange_2(entity1);
		TransportEntity entity3 = aKeyExchange.keyExchange_3(entity2);
		bKeyExchange.keyExchange_4(entity3);
	}

	public static class Signature {
		BigInteger r;
		BigInteger s;

		public Signature(BigInteger r, BigInteger s) {
			this.r = r;
			this.s = s;
		}

		public String toString() {
			return r.toString(16) + "," + s.toString(16);
		}
	}
}

SM3 雜湊算法

package org.pzone.crypto;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;

/**
 * SM3雜湊算法實現
 */
public class SM3 {

    private static char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
            '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    private static final String ivHexStr = "7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e";
    private static final BigInteger IV = new BigInteger(ivHexStr.replaceAll(" ",
            ""), 16);
    private static final Integer Tj15 = Integer.valueOf("79cc4519", 16);
    private static final Integer Tj63 = Integer.valueOf("7a879d8a", 16);
    private static final byte[] FirstPadding = {(byte) 0x80};
    private static final byte[] ZeroPadding = {(byte) 0x00};

    private static int T(int j) {
        if (j >= 0 && j <= 15) {
            return Tj15.intValue();
        } else if (j >= 16 && j <= 63) {
            return Tj63.intValue();
        } else {
            throw new RuntimeException("data invalid");
        }
    }

    private static Integer FF(Integer x, Integer y, Integer z, int j) {
        if (j >= 0 && j <= 15) {
            return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
        } else if (j >= 16 && j <= 63) {
            return Integer.valueOf((x.intValue() & y.intValue())
                    | (x.intValue() & z.intValue())
                    | (y.intValue() & z.intValue()));
        } else {
            throw new RuntimeException("data invalid");
        }
    }

    private static Integer GG(Integer x, Integer y, Integer z, int j) {
        if (j >= 0 && j <= 15) {
            return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
        } else if (j >= 16 && j <= 63) {
            return Integer.valueOf((x.intValue() & y.intValue())
                    | (~x.intValue() & z.intValue()));
        } else {
            throw new RuntimeException("data invalid");
        }
    }

    private static Integer P0(Integer x) {
        return Integer.valueOf(x.intValue()
                ^ Integer.rotateLeft(x.intValue(), 9)
                ^ Integer.rotateLeft(x.intValue(), 17));
    }

    private static Integer P1(Integer x) {
        return Integer.valueOf(x.intValue()
                ^ Integer.rotateLeft(x.intValue(), 15)
                ^ Integer.rotateLeft(x.intValue(), 23));
    }

    private static byte[] padding(byte[] source) throws IOException {
        if (source.length >= 0x2000000000000000l) {
            throw new RuntimeException("src data invalid.");
        }
        long l = source.length * 8;
        long k = 448 - (l + 1) % 512;
        if (k < 0) {
            k = k + 512;
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(source);
        baos.write(FirstPadding);
        long i = k - 7;
        while (i > 0) {
            baos.write(ZeroPadding);
            i -= 8;
        }
        baos.write(long2bytes(l));
        return baos.toByteArray();
    }

    private static byte[] long2bytes(long l) {
        byte[] bytes = new byte[8];
        for (int i = 0; i < 8; i++) {
            bytes[i] = (byte) (l >>> ((7 - i) * 8));
        }
        return bytes;
    }

    public static byte[] hash(byte[] source) throws IOException {
        byte[] m1 = padding(source);
        int n = m1.length / (512 / 8);
        byte[] b;
        byte[] vi = IV.toByteArray();
        byte[] vi1 = null;
        for (int i = 0; i < n; i++) {
            b = Arrays.copyOfRange(m1, i * 64, (i + 1) * 64);
            vi1 = CF(vi, b);
            vi = vi1;
        }
        return vi1;
    }

    private static byte[] CF(byte[] vi, byte[] bi) throws IOException {
        int a, b, c, d, e, f, g, h;
        a = toInteger(vi, 0);
        b = toInteger(vi, 1);
        c = toInteger(vi, 2);
        d = toInteger(vi, 3);
        e = toInteger(vi, 4);
        f = toInteger(vi, 5);
        g = toInteger(vi, 6);
        h = toInteger(vi, 7);

        int[] w = new int[68];
        int[] w1 = new int[64];
        for (int i = 0; i < 16; i++) {
            w[i] = toInteger(bi, i);
        }
        for (int j = 16; j < 68; j++) {
            w[j] = P1(w[j - 16] ^ w[j - 9] ^ Integer.rotateLeft(w[j - 3], 15))
                    ^ Integer.rotateLeft(w[j - 13], 7) ^ w[j - 6];
        }
        for (int j = 0; j < 64; j++) {
            w1[j] = w[j] ^ w[j + 4];
        }
        int ss1, ss2, tt1, tt2;
        for (int j = 0; j < 64; j++) {
            ss1 = Integer
                    .rotateLeft(
                            Integer.rotateLeft(a, 12) + e
                                    + Integer.rotateLeft(T(j), j), 7);
            ss2 = ss1 ^ Integer.rotateLeft(a, 12);
            tt1 = FF(a, b, c, j) + d + ss2 + w1[j];
            tt2 = GG(e, f, g, j) + h + ss1 + w[j];
            d = c;
            c = Integer.rotateLeft(b, 9);
            b = a;
            a = tt1;
            h = g;
            g = Integer.rotateLeft(f, 19);
            f = e;
            e = P0(tt2);
        }
        byte[] v = toByteArray(a, b, c, d, e, f, g, h);
        for (int i = 0; i < v.length; i++) {
            v[i] = (byte) (v[i] ^ vi[i]);
        }
        return v;
    }

    private static int toInteger(byte[] source, int index) {
        StringBuilder valueStr = new StringBuilder("");
        for (int i = 0; i < 4; i++) {
            valueStr.append(hexDigits[(byte) ((source[index * 4 + i] & 0xF0) >> 4)]);
            valueStr.append(hexDigits[(byte) (source[index * 4 + i] & 0x0F)]);
        }
        return Long.valueOf(valueStr.toString(), 16).intValue();

    }

    private static byte[] toByteArray(int a, int b, int c, int d, int e, int f,
                                      int g, int h) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
        baos.write(toByteArray(a));
        baos.write(toByteArray(b));
        baos.write(toByteArray(c));
        baos.write(toByteArray(d));
        baos.write(toByteArray(e));
        baos.write(toByteArray(f));
        baos.write(toByteArray(g));
        baos.write(toByteArray(h));
        return baos.toByteArray();
    }

    public static byte[] toByteArray(int i) {
        byte[] byteArray = new byte[4];
        byteArray[0] = (byte) (i >>> 24);
        byteArray[1] = (byte) ((i & 0xFFFFFF) >>> 16);
        byteArray[2] = (byte) ((i & 0xFFFF) >>> 8);
        byteArray[3] = (byte) (i & 0xFF);
        return byteArray;
    }
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n = 256 + n;
        int d1 = n / 16;
        int d2 = n % 16;
        return ""+hexDigits[d1] + hexDigits[d2];
    }

	public static String byteArrayToHexString(byte[] b) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            resultSb.append(byteToHexString(b[i]));
        }
        return resultSb.toString();
    }

    public static void main(String[] args) throws IOException {
        System.out.println(SM3.byteArrayToHexString(SM3.hash("test sm3 hash".getBytes())));
        System.out.println(SM3.byteArrayToHexString(SM3.hash("test sm3 hash".getBytes())));
    }
}

測試類

package org.pzone.crypto;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;

public class TestSM2 {
    public static void main(String[] args) {
        SM2 x = new SM2();
        SM2KeyPair keys = x.generateKeyPair();
        ECPoint pubKey = keys.getPublicKey();
        BigInteger privKey = keys.getPrivateKey();
        String originData="beautiful";
        System.out.println("原始數據:"+originData);
        byte[] data = x.encrypt(originData, pubKey);
        System.out.println("encrypt加密後的數據: " + data);
        String origin = x.decrypt(data, privKey);
        System.out.println("decrypt解密後的數據: " + origin);
        x.exportPrivateKey(privKey,"D:\\hello.txt");
    }
}


  1.生成密鑰對

  2.簽名與驗籤

  3.密鑰協商

  4.公鑰加解密

雜湊算法採用----SM3 密鑰派生算法----國密辦文檔中的KDF實現

相關文章
相關標籤/搜索