国密算法代码案列
SM2、SM4 java工具类使用,提供常见签名、验签静态方法;代码生成公私钥(生产环境不建议)。
编写pom文件
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.60</version>
</dependency>
SM2算法案列
SM2Support类
import org.bouncycastle.asn1.*;
import org.bouncycastle.jce.provider.JCEECPrivateKey;
import org.bouncycastle.jce.provider.JCEECPublicKey;
import org.bouncycastle.util.encoders.Hex;
import java.io.IOException;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECFieldFp;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.util.Arrays;
/**
* create by Cliven on 2018-07-31 13:48
* 解析SM2算法的相关参数的工具
*
* @author Cliven
* 其中参数参考《SM2椭圆曲线公钥密码算法推荐曲线参数》
*/
public class SM2Support {
/**
* SM2椭圆曲线公钥密码推荐参数
*/
public static final byte P[] = {
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
};
public static final byte A[] = {
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC
};
public static final byte B[] = {
(byte) 0x28, (byte) 0xE9, (byte) 0xFA, (byte) 0x9E, (byte) 0x9D, (byte) 0x9F, (byte) 0x5E, (byte) 0x34,
(byte) 0x4D, (byte) 0x5A, (byte) 0x9E, (byte) 0x4B, (byte) 0xCF, (byte) 0x65, (byte) 0x09, (byte) 0xA7,
(byte) 0xF3, (byte) 0x97, (byte) 0x89, (byte) 0xF5, (byte) 0x15, (byte) 0xAB, (byte) 0x8F, (byte) 0x92,
(byte) 0xDD, (byte) 0xBC, (byte) 0xBD, (byte) 0x41, (byte) 0x4D, (byte) 0x94, (byte) 0x0E, (byte) 0x93
};
public static final byte GX[] = {
(byte) 0x32, (byte) 0xC4, (byte) 0xAE, (byte) 0x2C, (byte) 0x1F, (byte) 0x19, (byte) 0x81, (byte) 0x19,
(byte) 0x5F, (byte) 0x99, (byte) 0x04, (byte) 0x46, (byte) 0x6A, (byte) 0x39, (byte) 0xC9, (byte) 0x94,
(byte) 0x8F, (byte) 0xE3, (byte) 0x0B, (byte) 0xBF, (byte) 0xF2, (byte) 0x66, (byte) 0x0B, (byte) 0xE1,
(byte) 0x71, (byte) 0x5A, (byte) 0x45, (byte) 0x89, (byte) 0x33, (byte) 0x4C, (byte) 0x74, (byte) 0xC7
};
public static final byte GY[] = {
(byte) 0xBC, (byte) 0x37, (byte) 0x36, (byte) 0xA2, (byte) 0xF4, (byte) 0xF6, (byte) 0x77, (byte) 0x9C,
(byte) 0x59, (byte) 0xBD, (byte) 0xCE, (byte) 0xE3, (byte) 0x6B, (byte) 0x69, (byte) 0x21, (byte) 0x53,
(byte) 0xD0, (byte) 0xA9, (byte) 0x87, (byte) 0x7C, (byte) 0xC6, (byte) 0x2A, (byte) 0x47, (byte) 0x40,
(byte) 0x02, (byte) 0xDF, (byte) 0x32, (byte) 0xE5, (byte) 0x21, (byte) 0x39, (byte) 0xF0, (byte) 0xA0
};
/**
* 阶
*/
public static final byte N[] = {
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
(byte) 0x72, (byte) 0x03, (byte) 0xDF, (byte) 0x6B, (byte) 0x21, (byte) 0xC6, (byte) 0x05, (byte) 0x2B,
(byte) 0x53, (byte) 0xBB, (byte) 0xF4, (byte) 0x09, (byte) 0x39, (byte) 0xD5, (byte) 0x41, (byte) 0x23
};
/**
* 基点G = (Xg, Yg)
*/
public static final ECPoint G_POINT = new ECPoint(new BigInteger(1, GX), new BigInteger(1, GY));
private static final EllipticCurve EllC = new EllipticCurve(new ECFieldFp(new BigInteger(1, P)), new BigInteger(1, A), new BigInteger(1, B));
public static final java.security.spec.ECParameterSpec SM2_SPEC =
new java.security.spec.ECParameterSpec(EllC, G_POINT, new BigInteger(1, N), 1);
/**
* 解析SM2 类型的公钥,并构造公钥对象
*
* @param publicKeyBytes 公钥字节串 格式为 X || Y
* @return 公钥对象
* @author Cliven
* @since 2018-07-31 14:04:16
*/
public static ECPublicKey parseSM2PublicKey(byte[] publicKeyBytes) {
if (publicKeyBytes == null || publicKeyBytes.length == 0) {
throw new IllegalArgumentException("publicKeyBytes is blank!");
}
byte[] pX = new byte[32];
byte[] pY = new byte[32];
// 从公钥字节中取出两个坐标值字节
System.arraycopy(publicKeyBytes, 0, pX, 0, 32);
System.arraycopy(publicKeyBytes, 32, pY, 0, 32);
// 将坐标值字节转为大数字
BigInteger bnX = new BigInteger(1, pX);
BigInteger bnY = new BigInteger(1, pY);
// 构造公钥点坐标 Pa = (Xa, Ya)
ECPoint pubPoint = new ECPoint(bnX, bnY);
// 构造椭圆曲线公钥
java.security.spec.ECPublicKeySpec ecPublicKeySpec = new java.security.spec.ECPublicKeySpec(pubPoint, SM2_SPEC);
return new JCEECPublicKey("SM2", ecPublicKeySpec);
}
/**
* 解析SM2 类型的私钥数据,并且构造私钥对象
*
* @param privateKeyBytes 私钥字节串
* @return 私钥对象
* @author Cliven
* @since 2018-07-31 14:17:31
*/
public static ECPrivateKey parseSM2PrivateKey(byte[] privateKeyBytes) {
if (privateKeyBytes == null || privateKeyBytes.length == 0) {
throw new IllegalArgumentException("privateKeyBytes is blank!");
}
byte[] pD = new byte[32];
// 从私钥数据中截取出私钥数据 k, k 属于 [1, n - 1]
System.arraycopy(privateKeyBytes, privateKeyBytes.length - 32, pD, 0, 32);
BigInteger bnD = new BigInteger(1, pD);
// 构造椭圆曲线私钥
java.security.spec.ECPrivateKeySpec ecPriSpec = new java.security.spec.ECPrivateKeySpec(bnD, SM2_SPEC);
return new JCEECPrivateKey("SM2", ecPriSpec);
}
/**
* 获取 X||Y 格式的公钥字节串
*
* @param publicKey 公钥对象
* @return X||Y 格式的公钥字节串
* @author Cliven
* @since 2018-07-31 10:42:43
*/
public static byte[] getPurePubKey(PublicKey publicKey) throws IOException {
ASN1Sequence sequence = ASN1Sequence.getInstance(publicKey.getEncoded());
// 取出公钥所在字段
byte[] asn1PubKeySrc = sequence.getObjectAt(1).toASN1Primitive().getEncoded();
DERBitString pubKeyBitString = DERBitString.getInstance(asn1PubKeySrc);
byte[] puk = pubKeyBitString.getBytes();
// 公钥格式为 04||X||Y (04为16进制字符,参考《GM/64-2012》)
return Arrays.copyOfRange(puk, 1, puk.length);
}
/**
* 获取ASN1Encodable 后32Byte,作为数据
*
* @param encodable 可编码的ASN1对象
* @return 后32byte数据
* @throws IOException 获取字节码时异常
* @author Cliven
* @since 2018-07-31 09:49:28
*/
public static byte[] getSignValuePair(ASN1Encodable encodable) throws IOException {
byte[] out = new byte[32];
ASN1Integer pair = ASN1Integer.getInstance(encodable.toASN1Primitive().getEncoded());
byte[] valueBytes = pair.getValue().toByteArray();
System.arraycopy(valueBytes, valueBytes.length - 32, out, 0, 32);
return out;
}
/**
* r || s 类型的数据结构的字节串转ASN1的 字节串
*
* @param pureData r || s 类型的数据
* @return ASN1编码的字节串
* @throws IOException 获取ASN1编码异常、
* @author Cliven
* @since 2018-07-31 16:13:21
*/
public static byte[] getPureSignData2ASN1(byte[] pureData) throws IOException {
byte[] r = Arrays.copyOfRange(pureData, 0, 32);
byte[] s = Arrays.copyOfRange(pureData, 32, pureData.length);
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(new ASN1Integer(new BigInteger(1, r)));
vector.add(new ASN1Integer(new BigInteger(1, s)));
DERSequence sequence = new DERSequence(vector);
return sequence.getEncoded();
}
/**
* 16进制字符串转int
*
* @param hex 16进制字符串
* @return 数字
* @author Cliven
* @since 2018-08-01 09:24:56
*/
public static int hexStr2int(String hex) {
byte[] len = Hex.decode(hex);
return new BigInteger(1, len).intValue();
}
}
SM2Util类
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
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.jcajce.spec.SM2ParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
public class SM2Util {
private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
/**
* 签名
* @param content
* @param userId
* @param privateKey
* @return
*/
public static byte[] signSm3WithSm2(byte[] content, byte[] userId, PrivateKey privateKey){
try {
SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);
Signature signer = Signature.getInstance("SM3withSM2", new BouncyCastleProvider());
signer.setParameter(parameterSpec);
signer.initSign(privateKey, new SecureRandom());
signer.update(content, 0, content.length);
byte[] sig = signer.sign();
return sig;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 验签
* @param content
* @param userId
* @param sign
* @param publicKey
* @return
*/
public static boolean verifySm3WithSm2(byte[] content, byte[] userId, byte[] sign, PublicKey publicKey){
try {
SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);
Signature verifier = Signature.getInstance("SM3withSM2", new BouncyCastleProvider());
verifier.setParameter(parameterSpec);
verifier.initVerify(publicKey);
verifier.update(content, 0, content.length);
return verifier.verify(sign);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* c1||c2||c3 加密
* @param data
* @param key
* @return
*/
public static byte[] sm2EncryptOld(byte[] data, PublicKey key){
BCECPublicKey localECPublicKey = new BCECPublicKey((ECPublicKey) key, null);
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), ecDomainParameters);
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
try {
return sm2Engine.processBlock(data, 0, data.length);
} catch (InvalidCipherTextException e) {
throw new RuntimeException(e);
}
}
/**
* c1||c2||c3 解密
* @param data
* @param key
* @return
*/
public static byte[] sm2DecryptOld(byte[] data, PrivateKey key){
BCECPrivateKey localECPrivateKey = new BCECPrivateKey((ECPrivateKey) key, null);
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(localECPrivateKey.getD(), ecDomainParameters);
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(false, ecPrivateKeyParameters);
try {
return sm2Engine.processBlock(data, 0, data.length);
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
String pubHex = "0CA34C2BA39BFE44DD4DDCCE5452C07CF9F4F9575F7B59488A2DFC32971730F39F5CBE944A786F74AD323CB60DC3F2F2B50945F1C";
ECPublicKey ecPublicKey = SM2Support.parseSM2PublicKey(Hex.decode(pubHex));
String priHex = "989187BFC882562E7E7791ED5B49DFC2C3E8E8A0E4A183C3F0A040E81E32BDA4";
ECPrivateKey ecPrivateKey = SM2Support.parseSM2PrivateKey(Hex.decode(priHex));
String userId = "00003";
String content = "0467cf298677a207d5b9c2ba8950dbdab9c7c91652bb580cc16e74bf68d3cac9bb178b3f6c4afdb77443b9e079767bc0e8bf67492fcb7d3c39598718df65cc721fe273601f1b751bf7ec80c43e0e9fbf4db23564f32801882d5a8441c79f39423b5f69c656e4a0a24030d870a4dcb05385dad14c691fc52ae15b6466d7666752752c1c12cfda927f9b6";
// byte[] bytes = signSm3WithSm2(Hex.decode(content), userId.getBytes(), ecPrivateKey);
// System.out.println("签名16进制:"+ByteUtils.toHexString(bytes));
System.out.println(verifySm3WithSm2(Hex.decode(content), userId.getBytes(), Hex.decode("3044022078fdf53f7c474499959b3d88b65ff4f9c63d59d3364162d5e9b002206e6ff7ad1b0dca26e5827708f36bd42114cc11ef162fda2ea4e905c5"), ecPublicKey));
}
}
SM4Util类
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
public class SM4Util {
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;
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, new BouncyCastleProvider());
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, new BouncyCastleProvider());
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);
}
}