一、概述
- SM1-无具体实现
SM1作为一种对称加密算法,由于其算法细节并未公开,且主要在中国国内使用,因此在国际通用的加密库(如Bouncy Castle)中并不直接支持SM1算法。
SM1算法的具体实现涉及国家密码管理局的规范,通常需要使用国家指定的安全模块(如SSF33、SC1/SC2卡)或通过国家认证的加密硬件/软件产品来实现。
不过,如果你有合法授权并且在合规的环境下需要使用SM1算法,可能需要依赖特定的国产加密库或SDK,比如某些商用的密码机提供的SDK,这些SDK会封装好SM1的加解密接口供开发者调用。
但具体的实现代码和工具类因为涉及知识产权和国家安全规定,无法在这里直接提供。
- SM2
非对称加密、签名算法
加密
解密
加签
验签
可替换
RSA
- SM3
签名算法 杂凑算法
计算哈希值
加签
验签
可替换
MD5
、Hmac
- SM4
对称加解密密算法
加密
解密
可替换
DES
- SM9-无具体实现
目前,标准的Java库如Bouncy Castle并不直接支持SM9算法;
因此可能需要使用特定的国密算法支持库或遵循国家密码管理局推荐的实现;
准备-添加依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.64</version>
</dependency>
二、国密详解
2.1 SM2实现
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
/**
* @author xch
* SM2实现工具类
*/
public class SM2 {
/*
* 这行代码是在Java中用于向安全系统添加Bouncy Castle安全提供器的。
* Bouncy Castle是一个流行的开源加密库,它提供了许多密码学算法和安全协议的实现。
* 通过调用Security.addProvider并传入BouncyCastleProvider对象,你可以注册Bouncy Castle提供的安全服务和算法到Java的安全框架中。
* 这样一来,你就可以在你的应用程序中使用Bouncy Castle所提供的加密算法、密钥生成和管理等功能。
*/
static {
Security.addProvider(new BouncyCastleProvider());
}
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
keyPairGenerator.initialize(new ECGenParameterSpec("sm2p256v1"));
return keyPairGenerator.generateKeyPair();
}
public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("SM2", "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
public static byte[] decrypt(PrivateKey privateKey, byte[] encryptedData) throws Exception {
Cipher cipher = Cipher.getInstance("SM2", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encryptedData);
}
public static byte[] sign(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
public static boolean verifySignature(PublicKey publicKey, byte[] data, byte[] signature) throws Exception {
Signature sig = Signature.getInstance("SM3withSM2", "BC");
sig.initVerify(publicKey);
sig.update(data);
return sig.verify(signature);
}
public static void main(String[] args) throws Exception {
// 将公钥私钥字符串转换为PublicKey和PrivateKey对象
KeyPair keyPair = generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
System.out.println("Public Key: " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));
PrivateKey privateKey = keyPair.getPrivate();
System.out.printf("Private Key: " + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
// 待加密数据
byte[] dataToEncrypt = "Hello, SM2!".getBytes();
// 加密
byte[] encryptedData = encrypt(publicKey, dataToEncrypt);
System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encryptedData));
// 解密
byte[] decryptedData = decrypt(privateKey, encryptedData);
System.out.println("Decrypted: " + new String(decryptedData));
// 加签
byte[] signature = sign(privateKey, dataToEncrypt);
System.out.println("Signature: " + Base64.getEncoder().encodeToString(signature));
// 验签
boolean isVerified = verifySignature(publicKey, dataToEncrypt, signature);
System.out.println("Signature verified: " + isVerified);
}
}
2.2 SM3实现
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;
import java.nio.charset.StandardCharsets;
import java.security.Security;
/**
* @author xch
* 2024/6/25 16:29
*/
public class SM3 {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* sm3计算哈希值
* @param hashString 待hash信息
* @return 签名
*/
public static String hash(String hashString) {
byte[] bytes = hashString.getBytes();
byte[] result = new byte[32];
SM3Digest sm3 = new SM3Digest();
sm3.update(bytes, 0, bytes.length);
sm3.doFinal(result, 0);
return Hex.toHexString(result);
}
/**
* sm3Hmac算法加签
* @param signStr 待加签字符串
* @param key key
* @return 返回加密后,固定长度=32的16进制字符串
*/
public static String sm3HmacSign(String signStr,String key){
// 将返回的hash值转换成16进制字符串
String resultHexString = "";
// 将字符串转换成byte数组
byte[] srcData = signStr.getBytes(StandardCharsets.UTF_8);
// 调用hash()
byte[] resultHash = sm3Hmac(srcData,key.getBytes(StandardCharsets.UTF_8));
// 将返回的hash值转换成16进制字符串
resultHexString = ByteUtils.toHexString(resultHash);
return resultHexString;
}
/**
* 通过密钥进行加密
* @explain 指定密钥进行加密
* @param key 密钥
* @param srcData 被加密的byte数组
* @return
*/
private static byte[] sm3Hmac(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;
}
public static void main(String[] args) {
String ss = "12345898888888";
String s = sm3HmacSign(ss, "1234l56");
String s1 = sm3HmacSign(ss, "1234l56");
System.out.println(s);
System.out.println(s1);
System.out.println(s.equals(s1));
}
}
2.3 SM4实现
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;
/**
* SM4-GCM工具类
* @author xch
*/
public class SM4 {
/**
* 加密算法
*/
public static final String ALGORITHM = "SM4";
/**
* 加密工作模式:GCM
* 数据填充模式:PKCS5Padding
*/
public static final String ALGORITHM_MODEL_GCM_PADDING = "SM4/GCM/NoPadding";
/**
* 随机数的长度
*/
public static final int NONCE_LENGTH = 128;
static {
// 添加Bouncy Castle provider
Security.addProvider(new BouncyCastleProvider());
}
/**
* 使用SM4-GCM模式加密
*
* @param plainText 需要加密的内容
* @param keyBytes 密钥字节数组
* @param ivBytes 初始化向量字节数组
* @return 加密之后的内容
* @throws Exception 加密过程中可能发生的异常
*/
public static String encryptWithGCM(String plainText, byte[] keyBytes, byte[] ivBytes) throws Exception {
SecretKeySpec sm4Key = new SecretKeySpec(keyBytes, ALGORITHM);
GCMParameterSpec ivSpec = new GCMParameterSpec(NONCE_LENGTH, ivBytes);
Cipher cipher = Cipher.getInstance(ALGORITHM_MODEL_GCM_PADDING, "BC");
cipher.init(Cipher.ENCRYPT_MODE, sm4Key, ivSpec);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
/**
* 使用SM4-GCM模式解密
*
* @param cipherText 需要解密的内容
* @param keyBytes 密钥字节数组
* @param ivBytes 初始化向量字节数据
* @return 解密之后内容
* @throws Exception 解密过程中可能发生的异常
*/
public static String decryptWithGCM(String cipherText, byte[] keyBytes, byte[] ivBytes) throws Exception {
SecretKeySpec sm4Key = new SecretKeySpec(keyBytes, ALGORITHM);
GCMParameterSpec ivSpec = new GCMParameterSpec(NONCE_LENGTH, ivBytes);
Cipher cipher = Cipher.getInstance(ALGORITHM_MODEL_GCM_PADDING, "BC");
cipher.init(Cipher.DECRYPT_MODE, sm4Key, ivSpec);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(cipherText));
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
/**
* 使用SecureRandom生成指定长度的密钥或IV
*
* @param length 密钥或IV的长度(字节数)
* @return 生成的随机字节数组
*/
public static byte[] generateKey(int length) {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[length];
secureRandom.nextBytes(bytes);
return bytes;
}
/**
* 生成指定长度的初始化向量(IV)
*
* @param length IV的长度(字节数)
* @return 生成的随机字节数组
*/
public static byte[] generateIV(int length) {
// IV的生成方式与密钥相同,使用SecureRandom
return generateKey(length);
}
public static void main(String[] args) throws Exception {
String plainText = "1234测试567";
// 使用随机的
byte[] key = generateKey(16);
byte[] iv = generateIV(16);
String cipherText1 = encryptWithGCM(plainText, key, iv);
System.out.println("加密后:" + cipherText1);
String decryptedText1 = decryptWithGCM(cipherText1, key, iv);
System.out.println("解密后:" + decryptedText1);
}
}
三、聚合工具类SMUtil
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
/**
* 国密
* <p>
* SM1-无具体实现
* SM1作为一种对称加密算法,由于其算法细节并未公开,且主要在中国国内使用,因此在国际通用的加密库(如Bouncy Castle)中并不直接支持SM1算法。
* SM1算法的具体实现涉及国家密码管理局的规范,通常需要使用国家指定的安全模块(如SSF33、SC1/SC2卡)或通过国家认证的加密硬件/软件产品来实现。
* 不过,如果你有合法授权并且在合规的环境下需要使用SM1算法,可能需要依赖特定的国产加密库或SDK,比如某些商用的密码机提供的SDK,这些SDK会封装好SM1的加解密接口供开发者调用。
* 但具体的实现代码和工具类因为涉及知识产权和国家安全规定,无法在这里直接提供。
* <p>
* SM2-
* 非对称加密签名算法
* 加密 解密 加签 验签
* 可替换RSA
* <p>
* SM3-
* 签名算法 杂凑算法
* 加签 验签
* 可替换MD5 Hmac
* <p>
* SM4-
* 对称加解密密算法
* 加密 解密
* 可替换DES
* <p>
* SM9-无具体实现
* 目前,标准的Java库如Bouncy Castle并不直接支持SM9算法;
* 因此可能需要使用特定的国密算法支持库或遵循国家密码管理局推荐的实现;
*
* @author xch
* 2024/6/25 13:40
*/
public abstract class SMUtil {
/**
* SM1作为一种对称加密算法,由于其算法细节并未公开,且主要在中国国内使用,因此在国际通用的加密库(如Bouncy Castle)中并不直接支持SM1算法。
* SM1算法的具体实现涉及国家密码管理局的规范,通常需要使用国家指定的安全模块(如SSF33、SC1/SC2卡)或通过国家认证的加密硬件/软件产品来实现。
* 不过,如果你有合法授权并且在合规的环境下需要使用SM1算法,可能需要依赖特定的国产加密库或SDK,比如某些商用的密码机提供的SDK,这些SDK会封装好SM1的加解密接口供开发者调用。
* 但具体的实现代码和工具类因为涉及知识产权和国家安全规定,无法在这里直接提供。
*
*/
abstract void sm1();
/**
* SM2生成 私钥和公钥
* @return 私钥和公钥
*/
public static KeyPair sm2GenerateKeyPair() {
try {
return SM2.generateKeyPair();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* sm2签名
* @param privateKey 私钥
* @param data 待签名内容
* @return 签名
*/
public static String sm2Sign(PrivateKey privateKey, String data) {
try {
byte[] sign = SM2.sign(privateKey, data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(sign);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* sm2验签
* @param publicKey 公钥
* @param data 待验签内容
* @param sign 签名
* @return 是否验签通过
*/
public static boolean sm2VerifySign(PublicKey publicKey, String data, String sign) {
try {
return SM2.verifySignature(publicKey, data.getBytes(StandardCharsets.UTF_8), Base64.getDecoder().decode(sign));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* sm2加密
* @param publicKey 公钥
* @param data 待加密内容
* @return 加密之后的内容
*/
public static String sm2Encrypt(PublicKey publicKey, String data) {
try {
byte[] encrypt = SM2.encrypt(publicKey, data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypt);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* sm2解密
* @param privateKey 私钥
* @param data 待解密内容
* @return 解密之后的内容
*/
public static String sm2Decrypt(PrivateKey privateKey, String data) {
try {
byte[] decrypt = SM2.decrypt(privateKey, Base64.getDecoder().decode(data));
return new String(decrypt, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* sm3 哈希值
* @param hashString 待哈希信息
* @return 签名
*/
public static String sm3Hash(String hashString) {
return SM3.hash(hashString);
}
/**
* sm3 加签计算
* @param signStr 待加签内容
* @param key 密钥
* @return 签名
*/
public static String sm3HmacSign(String signStr, String key){
return SM3.sm3HmacSign(signStr,key);
}
/**
* sm4加密
* 使用传输的密钥和初始化向量进行加密
* @param plainText 需要加密的内容
* @param key 密钥 长度必须为16
* @param iv 初始化向量 长度必须为16
* @return 加密之后的内容
* @throws Exception 加密过程中可能发生的异常
*/
public static String sm4Encrypt(String plainText, String key, String iv) throws Exception {
return SM4.encryptWithGCM(plainText, key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8));
}
/**
*
* sm4解密
* 使用传输的密钥和初始化向量进行加密
* @param plainText 需要加密的内容
* @param key 密钥 长度必须为16
* @param iv 初始化向量 长度必须为16
* @return 加密之后的内容
* @throws Exception 加密过程中可能发生的异常
*/
public static String sm4Decrypt(String plainText, String key, String iv) throws Exception {
return SM4.decryptWithGCM(plainText, key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8));
}
/**
* 目前,标准的Java库如Bouncy Castle并不直接支持SM9算法;
* 因此可能需要使用特定的国密算法支持库或遵循国家密码管理局推荐的实现;
*/
abstract void sm9();
public static void main(String[] args) throws Exception {
System.out.println("sm2 演示");
KeyPair keyPair = sm2GenerateKeyPair();
System.out.println(keyPair.getPrivate());
System.out.println(keyPair.getPublic());
String x = sm2Sign(keyPair.getPrivate(), "12谢谢哈哈");
System.out.println(sm2VerifySign(keyPair.getPublic(), "12谢谢哈哈", x));
String y = sm2Encrypt(keyPair.getPublic(), "12谢谢哈哈");
System.out.println(sm2Decrypt(keyPair.getPrivate(), y));
System.out.println("sm3 哈希 演示");
String s = sm3Hash("sm3哈哈");
System.out.println(s);
System.out.println("sm3加签演示");
String ss = "12345898888888";
String sm3HmacSign1 = sm3HmacSign(ss, "1234l56");
String sm3HmacSign2 = sm3HmacSign(ss, "1234l56");
System.out.println(sm3HmacSign1);
System.out.println(sm3HmacSign2);
System.out.println(sm3HmacSign1.equals(sm3HmacSign2));
System.out.println("sm4 演示");
String key = "jsimjrby3wqb7dbq";
String iv = "1234567890998889";
String sm4Encrypt = sm4Encrypt("123测试!@456",key, iv);
System.out.println(sm4Encrypt);
String sm4Decrypt = sm4Decrypt(sm4Encrypt, key, iv);
System.out.println(sm4Decrypt);
}
}