1. 什么是谷仓密钥.
每一个开通了开发者账号的客户,至少都会拥有一个默认应用。开发者可以配置属于应用的密钥对,包括公钥和私钥。
开发者使用应用接口传递业务数据时,都需要使用谷仓密钥进行数据加密。数据使用公钥进行加密,使用私钥解密。
建议开发者对于接口返回的数据进行解密核验校对,确认返回的数据是否正确,防止被第三方拦截攻击。
谷仓密钥使用SM2国密算法,前端后端代码均可以通过算法进行加解密校验。
1. 什么是谷仓密钥.
每一个开通了开发者账号的客户,至少都会拥有一个默认应用。开发者可以配置属于应用的密钥对,包括公钥和私钥。
开发者使用应用接口传递业务数据时,都需要使用谷仓密钥进行数据加密。数据使用公钥进行加密,使用私钥解密。
建议开发者对于接口返回的数据进行解密核验校对,确认返回的数据是否正确,防止被第三方拦截攻击。
谷仓密钥使用SM2国密算法,前端后端代码均可以通过算法进行加解密校验。
2. 数据加解密示例.
Java加解密
使用antherd进行加解密示例
import com.antherd.smcrypto.sm2.Keypair;
import com.antherd.smcrypto.sm2.Sm2;
import io.netty.util.internal.StringUtil;
public class SM2Encryptor {
/**
* 加密,使用公钥
*
* @param publicKey
* @param originalText
* @return
*/
public static String encryptText(String publicKey, String originalText) throws Exception {
if (StringUtil.isNullOrEmpty(publicKey)) {
throw new Exception("密钥不能为空...");
}
if (StringUtil.isNullOrEmpty(originalText)) {
throw new Exception("明文不能为空...");
}
try {
return Sm2.doEncrypt(originalText, publicKey);//HexUtil.encodeHexStr(cipherText); // 加密结果
} catch (Exception e) {
throw new Exception("加密错误:密钥不正确...");
}
}
/**
* 解密,使用私钥
*
* @param privateKey
* @param cipherText
* @return
*/
public static String decryptText(String privateKey, String cipherText) throws Exception {
if (StringUtil.isNullOrEmpty(privateKey)) {
throw new Exception("密钥不能为空...");
}
if (StringUtil.isNullOrEmpty(cipherText)) {
throw new Exception("明文不能为空...");
}
try {
return Sm2.doDecrypt(cipherText, privateKey); // new String(sm2.decrypt(sourceData,prvKey)); // 解密结果
} catch (Exception e) {
throw new Exception("解密错误:密钥不正确...");
}
}
/**
* 获取sm2密钥对,
*
* @return 返回String[];第0个为公钥,第1个为私钥
* @throws Exception
*/
public static String[] generateKeyPair() throws Exception {
try {
Keypair keypair = Sm2.generateKeyPairHex();
String[] result = new String[2];
if (keypair != null) {
result[0] = keypair.getPublicKey(); //公钥
result[1] = keypair.getPrivateKey(); // 私钥
}
return result;
} catch (Exception e) {
throw new Exception("生成密钥对失败...");
}
}
public static void main(String[] args) throws Exception {
//生成一对 公钥与私钥
String[] keys = generateKeyPair();
//公钥
String publicKey = keys[0];
System.out.println("公钥" + publicKey);
//私钥
String privateKey = keys[1];
System.out.println("私钥" + privateKey);
String str = "测试使用SM2加密、解密";
//加密字符串
String encryptText = SM2Encryptor.encryptText(publicKey, str);
System.out.println(encryptText);
//解密字符串
String decryptText = SM2Encryptor.decryptText(privateKey, encryptText);
System.out.println(decryptText);
}
}
使用Hutool的SM2工具类进行加解密示例
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.core.util.StrUtil;
/**使用指定的密钥对进行加解密测试*/
public void testDiySm2() {
String text = "伯牙善鼓琴,锺。子期善听。"
+ "伯牙鼓琴,志在登高山,锺子期曰:“善哉,峨峨兮若泰山!”志在流水,锺子期曰:“善哉,洋洋兮若江河!”伯牙所念,锺子期必得之。"
+ "伯牙游于泰山之阴,卒(cù)逢暴雨,正于岩下,心悲,乃援琴而鼓之。"
+ "初为霖雨之操,更造崩山之音。"
+ "曲每奏,锺子期辄穷其趣,伯牙乃舍琴而叹曰:“善哉,善哉,子之听夫!志想象犹吾心也,吾于何逃声哉?”";
// 公钥加密,私钥解密
String privateKey = "308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420f97cade46ccc9c24fa8e7cc6"
+ "d5f937d4f68e51e41ea5ed1b45f90d24ff109fb4a00a06082a811ccf5501822da14403420004dee120256e01bee93dc812d063c411"
+ "cfdef08abbc393157e3f341f5b95e65a6fe670208043c8d43f05f8ced762a74d9e6df757732f62feb1b17e04792074e233";
String publicKey = "3059301306072a8648ce3d020106082a811ccf5501822d03420004dee120256e01bee93dc812d063c411cfdef08abbc39"
+ "3157e3f341f5b95e65a6fe670208043c8d43f05f8ced762a74d9e6df757732f62feb1b17e04792074e233";
SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
System.out.println("加密后:" + encryptStr);
String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));
System.out.println("解密后:" + decryptStr);
}
VUE等Node.js项目加解密示例
在项目下执行脚本以安装依赖:
npm install --save sm-crypto
因为前端vue基本上使用统一的request.js,在项目的interceptor内处理相关参数的加解密比较简单,这里不再赘述。以下是核心代码示例:
const sm2 = require('sm-crypto').sm2 // 获取sm2对象
const cipherMode = 0 // 选择加密策略,1 - C1C3C2,0 - C1C2C3,默认为1
const sysPublicKey = '你对应的后台的公钥' // 系统后台公钥
const uiPrivateKey = '你自己前端的私钥' // 前端UI私钥
/**
* SM2加密string数据
* @param {string} data 原始数据
* @returns {string} 加密后数据
*/
export function getSm2DataHexByString(data) {
if (data && (typeof data === 'string') && data.constructor === String) {
return '04' + sm2.doEncrypt(data, sysPublicKey, cipherMode)
}
return null
}
/**
* SM2加密object数据
* @param {Object} data 原始数据
* @returns {string} 加密后数据
*/
export function getSm2DataHexByObject(data) {
if (data) {
return '04' + sm2.doEncrypt(JSON.stringify(data), sysPublicKey, cipherMode)
}
return null
}
/**
* SM2解密数据
* @param {string} dataHex 原始加密数据
* @returns {string} 解密后数据
*/
export function getSm2DataByString(dataHex) {
if (dataHex && (typeof dataHex === 'string') && dataHex.constructor === String) {
dataHex = dataHex.substring(2).toLocaleLowerCase()
return sm2.doDecrypt(dataHex, uiPrivateKey, cipherMode)
}
}
3. 参考文献.
国家密码管理局关于发布《SM2椭圆曲线公钥密码算法》公告[EB/OL].(2010-12-17) [2022-02-20].