인터페이스 보안 설계 원칙 중 하나는 데이터를 일반 텍스트로 전송할 수 없다는 것입니다.https의 필수 요청 외에도 인터페이스 데이터 암호화도 중요한 방법입니다.다음은 SM2 국가 비밀 알고리즘 암호화 및 복호화 사용에 대해 설명합니다.
SM2
여기서는 현재 프론트엔드와 백엔드 아키텍처의 분리를 기반으로 알고리즘을 올바르게 사용하여 데이터를 암호화하고 복호화하는 방법에 대해 간략히 소개하겠습니다 .
1. 백엔드 암호화 및 복호화
1.1 POM 의존성 가져오기
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
<!-- 加解密依赖包 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.71</version>
</dependency>
1.2 공개 키, 개인 키 키 쌍 생성
/**
* 生成公钥、私钥,这个保存好,尤其是私钥,切记不可泄漏
*/
public static void generateCommonKey() {
SM2 sm2 = SmUtil.sm2();
// 私钥:这个保存好,切记不要泄漏,真的泄露了就重新生成一下
byte[] privateKey = BCUtil.encodeECPrivateKey(sm2.getPrivateKey());
// 公钥:这个是前后端加密用的,不压缩选择带04的,不带04到时候前端会报错
byte[] publicKey = ((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false);
Console.log("公钥:\n{}", HexUtil.encodeHexStr(publicKey));
Console.log("私钥:\n{}", HexUtil.encodeHexStr(privateKey));
}
1.3 암호화 및 복호화
/**
* sm2明文加密
* PRIVATE_KEY:生成的私钥
* PUBLIC_KEY:生成的公钥
* @param data 加密前的明文
* @return 加密后的密文
*/
public static String encryptData(String data) {
SM2 sm2 = SmUtil.sm2(ECKeyUtil.toSm2PrivateParams(PRIVATE_KEY), ECKeyUtil.toSm2PublicParams(PUBLIC_KEY));
String encryptBcd = sm2.encryptBcd(data, KeyType.PublicKey);
// 这里的处理前端也可以处理,这个就看怎么约定了,其实都无伤大雅
if (StrUtil.isNotBlank(encryptBcd)) {
// 生成的加密密文会带04,因为前端sm-crypto默认的是1-C1C3C2模式,这里需去除04才能正常解密
if (encryptBcd.startsWith("04")) {
encryptBcd = encryptBcd.substring(2);
}
// 前端解密时只能解纯小写形式的16进制数据,这里需要将所有大写字母转化为小写
encryptBcd = encryptBcd.toLowerCase();
}
return encryptBcd;
}
/**
* sm2密文解密
* PRIVATE_KEY:生成的私钥
* PUBLIC_KEY:生成的公钥
* @param encryptData 加密密文
* @return 解密后的明文字符串
*/
public static String decryptData(String encryptData) throws Exception {
if (StrUtil.isBlank(encryptData)) {
throw new RuntimeException("解密串为空,解密失败");
}
SM2 sm2 = SmUtil.sm2(ECKeyUtil.toSm2PrivateParams(PRIVATE_KEY), ECKeyUtil.toSm2PublicParams(PUBLIC_KEY));
// BC库解密时密文开头必须带04,如果没带04则需补齐
if (!encryptData.startsWith("04")) {
encryptData = "04".concat(encryptData);
}
byte[] decryptFromBcd = sm2.decryptFromBcd(encryptData, KeyType.PrivateKey);
if (decryptFromBcd != null && decryptFromBcd.length > 0) {
return StrUtil.utf8Str(decryptFromBcd);
} else {
throw new Exception("密文解密失败");
}
}
2. 프런트엔드 암호화 및 복호화
2.1 NPM 패키지 종속성 설치
npm install --save sm-crypto
2.2 새 공개 sm2.js 만들기
const sm2 = require('sm-crypto').sm2;
// 加密策略,1 - C1C3C2,0 - C1C2C3,默认为1
const encryptMode = 1;
const publicUiKey = '后端生成的公钥';
const privateKey = '后端生成的私钥';
/**
* 加密数据
* @param {Object} data 明文数据
*/
export function encryptData(data) {
if (data && (typeof data === 'string') && (data.constructor === String)) {
return '04'.concat(sm2.doEncrypt(data, publicUiKey, encryptMode));
}
return data;
}
/**
* 加密对象数据
* @param {Object} data 对象明文
*/
export function encryptObjectData(data) {
if (data) {
return '04'.concat(sm2.doEncrypt(JSON.stringify(data), publicUiKey, encryptMode));
}
return data;
}
/**
* 解密数据
* @param {Object} dataHex 加密的密文数据
*/
export function decryptData(encryptData) {
if (encryptData && (typeof encryptData === 'string') && (encryptData.constructor === String)) {
const decryptData = sm2.doDecrypt(encryptData, privateKey, encryptMode);
return decryptData;
}
}
2.3 사용
import {
encryptData, decryptData} from "../../../request/sm2.js";
encryptData('明文');
decryptData('密文');